Archive for the ‘CLR’ Category

Érdekes .NET perf tapasztalat

Saturday, March 5th, 2011

Amikor profilerrel megnézünk egy .NET kódot sokszor megdöbbentő helyen lesz benne bottleneck.

Az alábbi kód 1% időt visz el egy nagyon processzorintenzív kódban:


if (bar.L == 0)

Ami ebben lassú, az a System.Decimal.op_Implicit(int32). A bar.L egy decimal. Érdekes, mi?
Mi a megoldás? A 0 legyen valóban decimal, de int, amit konvertálni kell:


if (bar.L == 0M)

1% kevés, de sok 1% már számít.

Csúnya multithreading hiba

Friday, January 21st, 2011

Double checked lockingnak indult, de bug lett belőle. Mit rontottam el?


private static volatile Dictionary<DateTime, Pair<TimeSpan>> holidayDays;
private static readonly object staticLock = new object();

private Dictionary<DateTime, Pair<TimeSpan>> GetHolidayDays()
{
    if (holidayDays == null)
    {
        lock (staticLock)
        {
            if (holidayDays == null)
            {
                holidayDays = new Dictionary<DateTime, Pair<TimeSpan>>();
                FillTradingHours(holidayDays, "HOL");
            }
        }
    }
    return holidayDays;
}

.NET Stringek lementése Memory Dumpból

Friday, November 26th, 2010

Mostanában elég sokat turkálok problémás webappok dumpjában. Leírom magamnak is, hogy lehet lementeni .NET stringeket a dumpból.
WinDbg dolgok következnek.

A nagy stringeket kilistáztatom a
!DumpHeap -min 100000 -type System.String
paranccsal.

Valamelyik heapről kinézek magamnak egyet, és az előbbi parancs kimenetének első oszlopában található címet felhasználva belenézek a du cím paranccsal.
Pl.
0:000> du 5ce31140
5ce31140 “*****<sitecore database=”SqlSer”
5ce31180 “ver”>.. <sc.variable name=”da”

Az elején a kuszaság (átírtam csillagokra, mert elrontotta a htmlt) a .NET Stringek adminisztrációja és az object header.
Ha szemre tetszik a string, akkor pl. ki akarom menteni fájlba.
Nézzük meg a szöveges rész előtti bináris részt (dd).

0:000> dd 5ce31140
5ce31140 793308ec 00040001 0002836c 0073003c
Az első 8 byte az object header (32 biten, 64-en ez asszem 16 byte), sync block és type leíró. Számunkra ez érdektelen. A 3. dword már érdekesebb, ez a string hozza karakterekben. Azaz bájtban ennek a duplája, mert 2 bytes unicode ábrázolást használ a .net.
Így stringünk hossza 0002836c*2 byte. A 0073003c már a < karakter (003c) és az s betű (0073), szokásos fordított Intel sorrendben.
A szöveges tartalom tehát a 5ce31140+0c címen kezdődik (headert átlépjük), és 0002836c*2 a hossza.
A tartalom fájlba írása már egyszerű:
.writemem c:\temp\s1.txt 5ce31140+0c L0002836c*2

Kedvencem a WinDbg, de ezt már mondtam. :)

Miért szeretem a dynamic típust reflection helyett?

Monday, May 17th, 2010

Ezért:


var allEntities = (IEnumerable)reposType.GetMethod("GetAll", new Type[] { typeof(string[]) }).Invoke(repos, new object[] { includes });

vs.


var allEntities = (IEnumerable)repos.GetAll(includes);

Emellett a dynamic vagy 10x gyorsabb, még akkor is, ha a reflectionnél cachelem a típusleírókat.

Naív ojjektumhasználat - sok szemét

Friday, November 27th, 2009

Két hete elkezdtem intenzíven használni a Visual Studio 2010-et, mivel szükségem volt a MemoryMappedFiles-ra a 4.0-s fwből. Ezt shared memoryként használom, amivel a laptopon kb. 1GByte/sec-kel tudok adatokat másolni két processz között, így kiváló cache-t tudtam építeni a segítségével. De most nem erről lesz szó.
A profilert is jelentősen továbbfejlesztették, van benne pl. rendes memória allokálás követés (lehet, hogy a régi is tudta ezt, akkor pardon).
Nézzük az alábbi képet:

A SecondInterval.Day property-t közel 6 milliószor hívtam meg, minden híváskor előállítva egy új ojjektumot, 140MByte-nyi szemetet hagyva magam után.

A naív implementáció így nézett ki:


public static SecondInterval Day
{
  get { return new SecondInterval(86400); }
}

Maga a típus egy egyszerű Whole Value pattern implementáció, egy sima immutable objektum, ráadásul class, így referenciális típus.

Nem memóriapazarló módon így néz ki a property:


private static readonly SecondInterval aDay = new SecondInterval(86400);
public static SecondInterval Day
{
    get { return aDay ; }
}

A readonly fontos, hogy nehogy valaki kiüsse a referenciámat, és berakjon egy sunyi másik időt reprezentáló ojjektumot a napi helyére. Az ojjektum immutable, ez fontos, másként semmit nem érne a readonly, a gyomrát lehetne piszkálgatni.
Amin még el lehetne gondolkodni, hogy struktúrává, value type-pá átalakítani a típust, csak akkor meg minden helyen ahol használom, másolni kellene az értékét. Mivel ez most belül egy 32 bites int, és 64 bit alatt futtatom, ahol a referenciák 64 bitesek, a másolással még mindig jobban járok. Úgyhogy lehet, hogy struct lesz, de előbb megnézem classként hogy muzsuikál.

Pár fejlettebb GC dolog

Wednesday, October 14th, 2009

Ezeket érdemes megnézni, érdekesek.

Ha nem szeretnénk, hogy egy rövid, de teljesítményzabáló kódunkat megszakítsa a GC: Latency Modes és Low-Latency GC in .NET 3.5.

Ha interopolunk, és várunk visszahívást nem managed oldalról, de félünk, hogy a GC felszabadítja az ojjektumunkat visszahívás előtt: GC.KeepAlive.

Ha nagyon sok memóriát zabáló appot írunk, és nem szeretnénk belefutni OutOfMemoryException-be, inkább lassítanánk a feldolgozáson: MemoryFailPoint.

Garbage Collection Notifications in .NET 3.5 SP1.

Jó Garbage Collector cikkek

Wednesday, October 14th, 2009

1, 2, 3, 4. Nem csak a szokásos, közismert dolgok, hanem mélyebbek is.
Pl. tudod-e, mekkora memóriát kér el induláskor a GC az OS-től? Hogy 3-féle GC is van (2-t ismer a legtöbb ember, már, aki egyáltalán hallott róla, hogy többféle GC is létezik)? Vagy miért száll el általában egy x86-os .net app 1.5G körüli memóriafelhasználásnál? Stb.

A jitter huncutságai

Tuesday, August 4th, 2009

Érdekes és furcsa dolgok történnek néha a .net jitter miatt, ami nem nagyon fordul elő nem managelt környezetben.

Itt ez a kis factoryka részlet:


public IBroker GetBroker()
{
    if (broker == null)
    {
        switch (BrokerMode)
        {
            case BrokerMode.Simulated:
                broker = new BrokerSimulated();
                break;
            case BrokerMode.IB:
                broker = IBBroker.Instance;
                break;
        }
    }
    return broker;
}

Amikor meghívtam ezt a sort:


BrokerFactory.Factory.GetBroker().SetInitialEquity(20000);

akkor furcsamód lefutott az élő broker, az IBBroker konstruktora. Pedig NEM az az ág futott le a switchben. Valószínűleg az történt, hogy a jitter hozzáért az IBBroker osztályhoz, ez pedig törvényszerűen kiváltotta a statikus konstruktor lefutását.

A megoldás pofonegyszerű (ha ismerjük az okot :), ki kell emelni a beteg típusra hivatkozást egy külön metódusba. Mivel a jitter metódusonként dolgozik, így addig tényleg nem nyúl a típusunkhoz, míg tényleg nem használjuk:


public IBroker GetBroker()
{
    if (broker == null)
    {
        switch (BrokerMode)
        {
            case BrokerMode.Simulated:
                broker = new BrokerSimulated();
                break;
            case BrokerMode.IB:
                broker = CreateIBBroker();
                break;
        }
    }
    return broker;
}

private static IBBroker CreateIBBroker()
{
    return IBBroker.Instance;
}

.NET teljesítményhangolási tapasztalatok 5.

Thursday, February 12th, 2009

Szeretjük a connection poolt, de nem mindig.

Tudjuk, hogy manapság nem illik sokáig nyitva tartani az adatbázis kapcsolatot, hanem bemegyünk, kihozzuk a szükséges adatokat, majd lezárjuk a kapcsolatot. Régen (n > 10 év) ez nem volt okos dolog, mert lassú volt a belépés-kilépés. Ezért találták ki a Connection Poolt, amit OLEDB-ben még Session Poolnak hívtak. Egykutya.
Ez arról szól, hogy a kliens oldali adatelérő komponens, pl. az ADO.NET SqlClient providere (System.Data.SqlClient névtér és kölkei) újrahasznosítja a lezárt kapcsolatot. Azaz mi SqlConnection.Close-t mondunk, ő viszont a háttérben nem zárja le a kapcsolatot az adatbázis felé. Ha ezek után újra meg akarjuk nyitni egy kapcsolatot az adatbázis felé, nem hoz létre új kapcsolatot a provider, hanem visszaad egy használtat a poolból. Miért jó ez? Nyilván egy tényleges kapcsolat kiépítése igen költséges, TCP csatorna vagy Named Pipe session felépítése, autentikáció, ecetera. Ezt nagyon sokszor megússzuk, hála a poolnak. A pool 4-8 perc közötti véletlenszerű időközönként azért lezárogatja a használt kapcsolatokat, ott ne zápuljanak.
Nyilvánvaló, hogy felhasználónként egyedi poolok vannak, így egy sysadmin által eldobott kapcsolatot nem adja oda nekem, lópicinek a provider, ez csúnya luk lenne.
Lehet azért így is aljaskodni az új bérlőkkel. Mi van, ha becsatlakozok, kiadok egy use másik adatbázist, majd röhögve lezárom a kapcsolatot? És ha egy-ket set opciót átírok, pl. SET LANGUAGE urdi? Mit lát a következő hívó, aki megkapja a használt kapcsolatot? Hát amit beállítottunk az előző lépésben. Azért ez durván hangzik, ugye? Szerencsére a helyzet az, hogy alapban tiszta környezetet kapunk, köszönhetően annak, hogy a provider pool manager-e végrehajt egy sp_reset_connection hívást mielőtt visszaadná nekünk a használt kapcsolatot. Hogy ez pontosan mit csinál, azt egyszer már leírtam, tessék megnézni a listát.
Azaz bár nem kell minden egyes kéréshez új kapcsolatot megnyitni, azért egy sp hívás csak történik pluszban a háttérben. SQL Profilerben látszik, hogy ez nagyon gyors, de azért ez mégis csak egy hálózati körülfordulás.
És most jön a trükk. Ha teljesen biztosak vagyunk benne, hogy nem tolunk ki a következő hívóval aki megkapja a használt kapcsolatunkat, akkor kikapcsolhatjuk a takarítást. A connection stringbe ezt kell beírni:


Connection Reset=false;

Nézzük meg az alábbi kódot:


SqlConnection conn = new SqlConnection("data source=.;initial catalog=ATS;Integrated security=true;");
SqlCommand cmd = new SqlCommand("sp_who", conn);
cmd.CommandType = CommandType.StoredProcedure;

for (int i = 0; i < 3; i++)
{
    conn.Open();
    cmd.ExecuteNonQuery();
    conn.Close();
}

Az SQL Profilerben ez látszik:


EventClass	TextData	SPID
Audit Login	-- network protocol: LPC
RPC:Completed	exec sp_who 	52
Audit Logout		52
RPC:Completed	exec sp_reset_connection 	52
Audit Login	-- network protocol: LPC
RPC:Completed	exec sp_who 	52
Audit Logout		52
RPC:Completed	exec sp_reset_connection 	52
Audit Login	-- network protocol: LPC
RPC:Completed	exec sp_who 	52
Audit Logout		52

Hoppá, ez nem az, amit vártunk! Csak 1 logint az elején és 1 logoutot a végén, közben meg az sp_resetconnectiont és az sp_who-nkat. Nem működik a pooling?
De. Csibész a profiler.
“The Audit Login event class indicates that a user has successfully logged in to Microsoft SQL Server. Events in this class are fired by new connections or by connections that are reused from a connection pool.”

Ha bekapcsoljuk a profilerben az Event Subclasst, kicsit tisztul a kép:


EventClass	TextData	SPID	EventSubClass
Audit Login	-- network protocol: LPC		1 - Nonpooled
RPC:Completed	exec sp_who 	52
Audit Logout		52	2 - Pooled
RPC:Completed	exec sp_reset_connection 	52
Audit Login	-- network protocol: LPC		2 - Pooled
RPC:Completed	exec sp_who 	52
Audit Logout		52	2 - Pooled
RPC:Completed	exec sp_reset_connection 	52
Audit Login	-- network protocol: LPC		2 - Pooled
RPC:Completed	exec sp_who 	52
Audit Logout		52	1 - Nonpooled

Szóval van pooling, csak a profiler ravaszkodik. Már kivert a víz. Viszont valami ebben zavar. A pooling ugye a kliensen van. Honnan tudja a szerver egy új parancs beérkeztekor az egy új parancsnak számít a kliens részéről a még nyitott kapcsolaton, vagy csak a poolból visszakapott kapcsolaton hajtanak végre egy új parancsot?
Szerintem ő mesterségesen szintetizálja a poolos login-logoutokat, az sp_reset_connection-ök beérkeztekor veszi őket körbe login-logout párossal, a feeling kedvéért. De ez csak tipp.

Kapcsoljuk ki a connection resetet:


SqlConnection conn = new SqlConnection(
    "data source=.;initial catalog=ATS;Integrated security=true;connection reset=false;");

Mit látunk a profilerben? Semmi nem változott! .NET fw. 2.0 felett nem lehet kikapcsolni az sp_reset_connection-t! Ezért nincs már a legújabb doksiban benne ez a beállítás.

Foly. köv.

.NET fw. platform invoke 64 biten

Monday, January 26th, 2009

Egy apró érdekesség. Van egy 32 bites windows dll, amit a gyártó még nem írt meg 64 bitre. Ezt .NET-ből a jól ismert DllImport attribútummal, PInvoke segítségével lehet meghívni. 64 bites gépen azonban alapértelmezetten a C# fordító olyan assemblyket generál, amelyek platform függetlenek, így 64 bites osen a jitter 64 bites gépikódot generál. Persze, így már nem tudja betölteni a 32 bites DLL-t a pinvoke, és elszáll a program hibával, amikor először hívnánk egy függvényt a DLL-ből. A megoldás egyszerű: meg kell jelölni a projektet, hogy x86-ra forduljon. Ettől az IL kód persze nem változik, de a metadatok között ott lesz a jelzés a jitternek, hogy tessék 32 bites kódot generálni.
A bosszantó ebben az esetben csak az, hogy azért lett volna jó a natív 64 bites memóriamodell, mert tényleg gigabájtnyi adatot akartam a memóriában kezelni.

Debugolás a .NET fw. forrása segítségével

Thursday, December 11th, 2008

Kaptam egy igen nehezen megközelíthető problémát, amelyben a fókusz a TAB-ra átlépett egyes controlokat. Nem egy triviális TabStop=false probléma volt.
VS 2005-ös projektekről van szó, átkonvertáltam őket 2008-ra, hogy tudjam a fw. forráskódját is debugolni. Az _NT_SYMBOL_PATH= nekem be van állítva a gépen a publikus os szimbólumokra (elsősorban ahhoz amikor WinDbgozok), emiatt nem tudtam a vsből .net fw.-öt debugolni, mert előbb lehúzza a stripped szimbólumokat, a teljeshez már hozzá se nyúl. Ezért egy bat-ból indítom a vs-t, előtte kiütve az eredeti _NT_SYMBOL_PATH-t.
Így már ment a fw. forrás debug, de mivel a clr az ngenelt optimalizált kódot töltötte be, ezért nagyon sok típus belseje nem látszik normálisan. Erre megoldás itt található. Le lehet tiltani, hogy a CLR az ngenelt kódot töltse be, így már rendesen lehet debugolni.
Lehetne, ha nem lenne elcsúszva némelyik forráskód a pdb-ben található sorszámoktól. Ilyenkor van az, hogy teljesen más sorokon lépkedünk végig, mint amit a source ablakban látunk, pl. kommenteken lépked végig a debugger.
A megoldás erre egyszerűbb volt, mint gondoltam volna: próbaképpen kitöröltem 3 sort pl. a Control.cs elejéből, így visszaállt a szinkron.
Maga az alapprobléma egyébként abból adódott, hogy egy kompozit Third Party Contol explicit letiltotta
a TAB-olást, a ControlStyle-ból kivéve a Selectable flaget.

Uninstalling .NET Framework 2.0 SP2 and 3.5 SP1 in Vista

Thursday, July 31st, 2008

Recently I had a run-time heap corruption problem in our software which is caused by some internal changes in the beta CLR and .NET Framework assemblies. This software uses lots of interop code, it become broken because of these changes in the fw.
So, I decided to uninstall the beta frameworks from my machine but it’s happened to be not such a trivial thing to do. I asked Aaron Stebner in Microsoft for some help who responded me kindly and promptly. Thanks for it. :)

Here is his answer:

“The original version of the .NET Framework 3.5 included the .NET Framework 2.0 SP1 and 3.0 SP1, but it created separate Add/Remove Programs entries for 2.0 SP1 and 3.0 SP1 so you could uninstall all of the pieces as long as you uninstalled in the exact order of 3.5, 3.0 SP1, 2.0 SP1. That same behavior should be the case for 3.5 SP1 as well.

One thing that might be different here - on Windows Vista and Windows Server 2008, the .NET Framework 2.0 and 3.0 are OS components, so they will not show up in Add/Remove Programs. If you are running one of these OS’s, you will need to use steps like this to remove the .NET Framework 2.0 SP2 and 3.0 SP2:

1. Uninstall the .NET Framework 3.5 SP1 product from the Programs and Features control panel
2. In the Programs and Features control panel, click the link on the left named View Installed Updates
3. In the list of installed updates, look for an item named Update for Microsoft Windows (KB948609) - this is the .NET Framework 3.0 SP2
4. Right-click on the item and choose Uninstall
5. In the list of installed updates, look for an item named Update for Microsoft Windows (KB948610) - this is the .NET Framework 2.0 SP2
6. Right-click on the item and choose Uninstall
7. Reboot”

So, the key to uninstall these beta bits from your Vista machine is to remove KB948609 and KB948610 hotfixes.

Apparently the fw. rollback healed our application. We will investigate this problem if it comes up in the RTM bits too (in few weeks I think). I hope it won’t. :)

Bocs, de ezt Aaron kérésére angolul írtam.
A lényeg, hogy ha Vistán le kell szedni az SQL 2008 RC0 vagy más által felrakott beta NET Framework 2.0 SP2-t és 3.5 SP1-et, akkor a KB948609 és KB948610 hotfixeket kell uninstallálni.

A nap híre: .NET Framework Library Source Code now available

Thursday, January 17th, 2008

Ma mindenkinél erről lehet majd hallani, örülünk, böngészünk, debugolunk.

Halmaz osztály a .NET Fw-ben

Saturday, August 25th, 2007

Nem új a dolog, de a LINQ kapcsán láttam meg a reflectorban ezt a jószágot: HashSet.

Nagyon örülünk, ez hiányzott a fwből. A fákra viszont kicsit még rámászhatnának, mert azok továbbra is hiányoznak…

Rossz sorrendű inicializálás .NET-ben?

Wednesday, July 25th, 2007

Javaban van egy közismert jelenség, ami miatt a Double Checked Locking nem működik jól, mégha az ötlet zseniális is.
Régóta érdekelt, hogy vajon ugyanez a probléma fennáll-e .NETben is? Belső infó szerint nem, legalábbis az MS CLR-ben nem.
Érdemes elolvasni a probléma okát is az előző cikkben, rövid.

StringBuilder vs. String

Thursday, October 26th, 2006

Sokan olvasták, hogy a StringBuilder a nyerő, ha több stringgel kell dolgozni, pl. összefűzni őket. Aztán itt meg is állnak, és már pár string esetén is boldogan izzítják a StringBuildert, és büszkék, hogy ez aztán a szakos kód.

De biztos így van ez?

Nem.

Meglepő módon a pl. a string.Join jóval gyorsabb, mint a StringBuilder.Append? Hogy mi? Sok elemre is? Igen.

Meglepőek néhol az eredmények, érdemes megnézni a cikket.

A szerzőnek abban is igaza van, hogy nagy stringeknél (több k vagy nagyobb) nagyon jó a StringBuilder, de kicsiknél, amik sokkal tipikusabbak, nem feltétlenül.

Az elmúlt hét tanulságai

Monday, July 3rd, 2006

Hajmeresztő (szerencsére csak szakmailag) időszakon vagyok lassan túl, ezért is nem blogoltam már régen.

Pár vegyes tanulság az elmúlt időszakból, zöme magamnak is emlékeztető:

  • A C/C++ secure stringkezelő (és egyéb) függvények nem úri huncutságok, és nem csak security szempontjából fontosak. A buffer overrun fogalom sok embernek a hekkeléssel forrt össze, pedig adatvesztések, instabilitások is gyakran következményei. Ha egyszer vannak rá normális függvények, és a kódok átírása se olyen vészes (tudom, mert átírtam vagy 100 függvényhívást az új függvényekre), akkor nem látok okot nem megtenni. Ahol a régi kód kussol, csendben elbassza a dolgokat az új visong. Ugyanez a helyzet a /GS kapcsolóval is (stack overrun ellenőrzés), valamint a /analyze opcióval (prefast) is. Igenis hallgatni kell rájuk, mert értelmes hibákat szúrnak ki.
  • Ha az IIS alatt nem megy egy website, Service Unavailable hibával nem indul a worker process, és 0×80004005-ös (Access Denied) hiba van az eventlogban, akkor az iis valahol nem tud írni vagy olvasni. Jelentős FileMon/Regmon erőfeszítéseim ellenére se jöttem rá mi hiányzik neki, de az aspnet_regiis -ga megadja a szükséges jogokat a futtatáshoz.
  • Ha egy osztály IDisposable, akkor kurvára meg kell hívni a Dispose metódust. Nem tudok elég nagy betűket rendelni ehhez:

Használd a C# usingot, bmeg!!!

Én a DirectoryServicessel szívtam meg, mivel az nemmanazsolt kódot és erőforrásokat hívokat a háttérben úgy fejreáll mint állat pár száz allokált objektum után, ha nem Disposolunk keményen.

  • A backup hasznos dolog. Ennek hiányában a GetDataBack-kel számottevő sikereket érhetünk el. A rendszergazda meg barátkozzon össze a kapa nyelével a megfelelő testrészén.