Archive for the ‘.NET’ Category

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.

Random orderby Linq és EF használatával

Monday, October 12th, 2009

Az SQL Serveren szokásos orderby newid() szerveroldali megoldást csak linq to SQL esetén tudjuk kihasználni, szerveroldali függvénnyel. Az EF-ben az ilyesmi nem megy:


... orderby Guid.NewID() ...

Ez az EF verzió még nem tudja lefordítani szerveroldali kifejezéssé az orderby kifejezését.
Viszont a random rendezést át lehet nyomni kliensoldalra, ha a lekérdezést “materializáluk” (AsEnumerable) előbb:


Random rnd = new Random();
(from s in ATSEntities.Instance.Symbol
select s).AsEnumerable().OrderBy(o => rnd.Next()));

Nem guidot használtam, hanem Randomot, az kisebb költségű, és az én célomra nem baj, ha csak pszeudo-random a sorrend.

Code Coverage for Concurrency

Thursday, October 8th, 2009

Mostanában rengeteg cikket olvasok az msdnből, rám fér, mert minden hónapban hozza a postás, de párat már ki se nyitottam. Szívom fel magam az oktatások közötti szünetekben, kicsit képbe kerüljek azokkal a cuccokkal, amik a 4.0-ban jönnek be.
A fenti cikk nagyon tetszett, arról ír, hogyan lehet megközelíteni egy többszálú app tesztelését.

Sandcastle és társai

Wednesday, September 30th, 2009

Az NDoc halott, van helyette SandCastle. Nagyon jó, imádom. Ezzel csinálják a VS doksiját is. Van még kétely valakiben?
Az is jó, hogy nem kell kézzel írnom a konfigját, mert van hozzá Sandcastle Help File Builder, ami hasonló GUI, mint az ndochoz volt.
Aztán, hogy ne kelljen sokat gépelni az xml kommenteket, ott a GhostDoc. Beépül a VS-be, és CTRL-ALT-D-vel létrehoz egy komment vázat az adott kódrészhez. Meglepően ügyesen, ha angol neveket használunk a kódban.
Mindhárom eszköz zseniális és INGYENES. Aki ezek után nem dokumentálja a kódját, annak 1-es. :)

C# XML kommentek

Wednesday, September 30th, 2009

Ha valaki szeretne rászánni 2 órát, hogy alaposan megértse, íme egy jó doksi hozzá.

Unraveling the Mysteries of .NET 2.0 Configuration

Monday, September 28th, 2009

Számomra rém unalmas téma, de ettől függetlenül nagyon fontos. Mivel elég gyéren dokumentálta le az ms, az itteni cikkek életmentőek lehetnek, akinek ezzel kell foglalkozni.

INVARIANT vs. ORDINAL újra

Wednesday, September 23rd, 2009

A korábban írt cikkhez kapcsolódó anyag, ebből már jobban átjön a különbség:
New Recommendations for Using Strings in Microsoft .NET 2.0.

* DO: Use StringComparison.Ordinal or OrdinalIgnoreCase for comparisons as your safe default for culture-agnostic string matching.
* DO: Use StringComparison.Ordinal and OrdinalIgnoreCase comparisons for increased speed.
* DO: Use StringComparison.CurrentCulture-based string operations when displaying the output to the user.
* DO: Switch current use of string operations based on the invariant culture to use the non-linguistic StringComparison.Ordinal or StringComparison.OrdinalIgnoreCase when the comparison is linguistically irrelevant (symbolic, for example).
* DO: Use ToUpperInvariant rather than ToLowerInvariant when normalizing strings for comparison.
* DON’T: Use overloads for string operations that don’t explicitly or implicitly specify the string comparison mechanism.
* DON’T: Use StringComparison.InvariantCulture-based string operations in most cases; one of the few exceptions would be persisting linguistically meaningful but culturally-agnostic data.

Ebből az jön le, hogy használd vagy az ordinalt, vagy a “kulturált” megoldást, de ha lehet ne az invariantot.

# Ordinal comparisons are string comparisons in which each byte of each string is compared without linguistic interpretation. This is essentially a C runtime strcmp. Thus, “windows” would not match “Windows.” Where the context dictates that strings should be matched exactly, or demands conservative matching policy, this comparison should be used. Additionally, ordinal comparisons are the fastest because they apply no linguistic rules when determining a result.

# Case insensitive ordinal comparisons are the next most conservative, and ignore most casing. Thus, “windows” would match “Windows.” When dealing with ASCII characters, this policy is equivalent to that of StringComparison.Ordinal, but with the usual ASCII casing ignored. Thus, any character in [A, Z] (\u0041-\u005A) matches the corresponding one in [a,z] (\u0061-\007A). Casing outside the ASCII range uses the invariant culture’s tables…

Oridinal, case sensitive bináris összehasonlítás, gyors, ok.
Oridinal, case insensitive ascii karakterek esetén egyenlőnek tekinti a kis-nagybetűket, más karakterek esetén átvált az invariantra. Praktikusan szerintem ez azt jelenti, hogy ordinal case insensitive == invariant case insensitive.
Update: Jogos az előbbi, mert: “Comparisons made using OrdinalIgnoreCase are behaviorally the composition of two calls: calling ToUpperInvariant on both string arguments, and doing an Ordinal comparison.”

Közelebbről:


using System;

class Program
{
    static void Main(string[] args)
    {
        string kisalma = "alma";
        string nagyalma = "Alma";
        Compare(kisalma, nagyalma);
        Console.WriteLine();
        kisalma = "őlma";
        nagyalma = "Őlma";
        Compare(kisalma, nagyalma);
    }

    private static void Compare(string a, string b)
    {
        Test(a, b, StringComparison.CurrentCulture);
        Test(a, b, StringComparison.CurrentCultureIgnoreCase);
        Test(a, b, StringComparison.InvariantCulture);
        Test(a, b, StringComparison.InvariantCultureIgnoreCase);
        Test(a, b, StringComparison.Ordinal);
        Test(a, b, StringComparison.OrdinalIgnoreCase);
    }

    private static void Test(string a, string b, StringComparison c)
    {
        int compRes = string.Compare(a, b, c);

        Console.WriteLine("{0}, {1} {2} {3}", c, a,
            compRes == 0 ? "==" : (compRes < 0 ? "<": ">"),
            b);
    }
}

CurrentCulture, alma < Alma
CurrentCultureIgnoreCase, alma == Alma
InvariantCulture, alma < Alma
InvariantCultureIgnoreCase, alma == Alma
Ordinal, alma > Alma
OrdinalIgnoreCase, alma == Alma

CurrentCulture, őlma < Őlma
CurrentCultureIgnoreCase, őlma == Őlma
InvariantCulture, őlma < Őlma
InvariantCultureIgnoreCase, őlma == Őlma
Ordinal, őlma > Őlma
OrdinalIgnoreCase, őlma == Őlma

Nem teljes a teszt, de látható, hogy az ordinal másként sorrendez, mint az invariant, nem nyelvi szempontok szerint, ezért is nem való GUI-hoz, de tudja, hogy a kis ő-nek a nagy Ő a párja. Az invariant se elég okos persze, mivel általános, nem ismeri egy adott nyelv szabályait, pl.


Thread.CurrentThread.CurrentCulture = new CultureInfo("hu-hu");
a = "cukor";
b = "csoki";
Compare(a, b);

CurrentCulture, cukor < csoki
CurrentCultureIgnoreCase, cukor < csoki
InvariantCulture, cukor > csoki
InvariantCultureIgnoreCase, cukor > csoki
Ordinal, cukor > csoki
OrdinalIgnoreCase, cukor > csoki

A cukor után van a csoki magyarban, mivel van csé betűnk. Az invariant értelemszerűen nem tud erről, ezért a csokit cé, es, okinak olvassa, és így is rendez.

Ezek után egyet kell értsek a cikkel, használjuk kulturált kultúrát nyelvi rendezéshez, fájl elérési utakhoz, ojjektum nevekhez, stb. oridinalt, az invariantot meg felejtsük el.
Legalábbis ma így látom. Comments welcome.

MethodBase.GetCurrentMethod Method

Wednesday, September 23rd, 2009

Ismerős? Pl. logoláshoz jól jöhet.

Comparison confusion: INVARIANT vs. ORDINAL

Tuesday, September 22nd, 2009

Elolvastam a fenti cikket, és nem jutottam sokkal előbbre. A Ordinal vs. többi az ok, de az Invariant nem jött át. Ha valakinek igen, írja meg kérem.

Resharper

Tuesday, September 22nd, 2009

A következő néhány hónapban egy rakat .NET oktatást tartok egy cégnek Marcell jóvoltából (ezúttal is köszönöm neki az üzletet).
A cégnél szabványosítani fogják a Resharpert, egy VS kiegészítő addint. Többször felraktam már a gépemre, de mindig levettem, mert idegesített, hogy mindent átszab a VS-ben. Én mindent shortcutokkal érek el, azokra jól ráül, aztán feljön a saját felülete. Ezzel az agyamra ment, sokat okoskodott, mindig leszedtem.
Most, hogy tanuljam, mire az oktatás jön, újra felraktam, és használom nap mint nap. Telepítés után azt kértem tőle, nem telepedjen rá minden shortcutra, így már egész jól megvagyunk.
Ami viszont igen kellemes meglepetés volt, hogy már két hibát is kiszúrt. Az egyikben egy paraméter értékét az ugyanolyan nevű mezőbe raktam bele, csak valahogy lemaradt a this. Triviális hiba, de nem tűnt fel a sok paraméter között. A másik kicsit ravaszabb.


Process[] p = Process.GetProcessesByName("...");
if (p == null) ...

Mi a hiba eben a kódban? A resharper jól kiszúrta, hogy az if kifejezése mindig true false. Miért? Így néz ki a GetProcessesByName vége:


Process[] array = new Process[list.Count];
list.CopyTo(array, 0);
return array;

Nulla hosszú tömb jön vissza, ha nincs ilyen nevű processz. Nem rakétatudomány, mégis könnyű elrontani. A Resharper meg észrevette. Megtartjuk, szeretjük.

Az IFormattable, IFormatProvider, ICustomFormatter működése

Monday, September 21st, 2009

Ebből pontosan meg lehet érteni, mi zajlik a háttérben.

Mire való az IEquatable?

Monday, September 21st, 2009

Ha value type-ot írsz, és azon gyors, boxolás nélküli egyenlőségvizsgálatot akarsz csinálni, akkor implementáld ezt az interfészt.
Másképp az object.Equals fut le, ami miatt boxolni kell. Hasonló a helyzet az IComparable-lel is.
Érdemes megnézni debuggerben, mikor-mi fut le, ha implementáljuk az interfészt, és ha kikommentezzük az implementációt:


using System;
using System.Collections.Generic;

internal struct MyStruct : IEquatable<MyStruct>
{
    public MyStruct(int a)
    {
        this.a = a;
    }

    private readonly int a;

    public override bool Equals(object obj)
    {
        return base.Equals(obj);
    }
    public override int GetHashCode()
    {
        return base.GetHashCode();
    }

    #region IEquatable<MyStruct> Members

    public bool Equals(MyStruct other)
    {
        return other.a == a;
    }

    #endregion
}
class Program
{
    static void Main()
    {
        List<MyStruct> l = new List<MyStruct> {new MyStruct(33)};
        l.Contains(new MyStruct(22));
    }
}

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;
}

Háttérszálból módosítható WPF adatköthető kollekció

Friday, July 10th, 2009

A WPF mint a legtöbb GUI nem szereti, ha más szálakról piszkálják. Sokszor azonban az adatokat más szálak készítik elő megjelenítésre, amit adatkötéssel szeretnénk átadni a GUI-nak. Ilyenkor azonban mindenféle szaftos hibákat fogunk kapni. Egy megoldás a problémára itt található (alsó 4 link), ahol a szerző értelmesen elmagyarázza a probléma okát és megoldását, majd a végén ad konkrét, azonnal használható kódot is.

A többszálú skálázás nehézségei

Thursday, June 11th, 2009

(Egyszer megírtam ezt a postot hosszabban, de a wordpress a sorozatos Save-ek ellenére elvesztette…)
Az utóbbi időben az időm jelentős részét a tőzsdei kereskedő programom írásával töltöm, és eközben tanulom az Entity Frameworköt (nagyon sarkos, hogy finoman fogalmazzak), illetve próbálom kihasználni egy erős, 8 processzoros rendszer képességeit.
Nyilvánvalóan a szálak számának növelésével elvileg szépen meg lehet hajtani a processzorokat. Ám furcsa mód még a backtest programom, ami szoros ciklusokban pörög se tudta jobban kihajtani a procikat, mint 20-30%. A kérdést kicsit megvakargatva kiderült, hogy a Garbage Collector elviszi az idő 80%-át is egyes esetekben, azaz pazarlom a memóriát, ezt át kell írni, illetve meg kell nézni, hogy server vagy workstation GC megy-e a háttérben. A server GC párhuzamosított, a workstation nem, így az lehet torlódási pont.
Hogy az-e, azt döntse el mindenki a következő két kép alapján: :)

SgmlReader

Saturday, May 30th, 2009

Ha htmlt kell kódból parsolni, akkor jól jöhet ez a kis lib. Annak idején egy ms jóember írta meg, de sokáig senki nem frissítette, most kézbe vette egy cég az ügyet, és vannak rendszeresen bugfix kiadások.

Arról szól a dolog, hogy nem teljesen korrekt html tartalmat is kiegyenget, mint a htmltidy, és utána már xml domban fel lehet dolgozni a tartalmat. Sokkal jobb megoldás, mint regexszel bíbelődni.

VS Profiler nem megy Hyper-V-s gépen

Monday, April 27th, 2009

Felraktam a gépemre a Hyper-V-t. A VSTS profilert akartam használni, de azt mondta, No data collected.
Szerencsére megtaláltam ezt:
VS2008 does not support profiling in virtual environments (VMWare, VPC, Hyper-V).

Ez a jelek szerint nem csak virtuális guest-ekre vonatkozik, hanem a hostra is. Kikapcsoltam a BIOS-ban a virtualizálás támogatást, és egyből elindult a profiler.

Ezentúl rebootkor el kell döntenem, guest-eket akarok futtatni, vagy profiler-ezni.

.NET teljesítményhangolási tapasztalatok 7. - Hatékony UNC fájlelérés

Thursday, February 26th, 2009

Hogyan közelítsük meg azt a problémát, hogy egy managed alkalmazásból naponta több milliószor el szeretnénk érni másik szerveren levő, viszonylag ritkán, pár percenként frissülő fájlokat, a lehető leghatékonyabb módon?

Először is érdemes tudni, hogy a workstation service, a windows fájok távoli elérésének kliense cache-el, az OS File Cache-ét használva, ami a helyi fájlokat is cache-eli. Ha azonban módosítják a megosztáson levő fájlt, kikapcsolja a cache-elése erre a fájlra, egészen addig, míg újra meg nem nyitjuk. Bővebben majd egy teljes cikkben is megemlékezek erről, amikor a Windows Cache részleteiről fogok írni (a technet site-on).

Szóval kapuk valamennyi teljesítménynövekedést az OS cache miatt, de mivel a fájl olvasása során a PInvoke-nak át kell mozgatni az adatokat a natív oldalról a managed oldalra, jelentős veszteségeket élünk meg azzal szemben, ha inprocess, managed memóriában tudnánk tartani a fájlok tartalmát, de kiütve őket onnan, ha megváltoznak.

Gondolom a legtöbben már hallottak a System.Web.Caching.CacheDependency típusról. Ezt alapvetően webalkalmazások használják, de 2.0 óta már egyéb appokban is használható. A segítségével bámulatosan egyszerűen lehet cache-elni a felolvasott fájlok tartalmát.


object o = HttpRuntime.Cache[cacheKey];
if (o != null)
{
    string s = (string)o;
}
else
{
    CacheDependency d = new CacheDependency(file);
    string s = ProtectedRead(file);
    HttpRuntime.Cache.Add(cacheKey, s, d,
        Cache.NoAbsoluteExpiration,
        Cache.NoSlidingExpiration, CacheItemPriority.Normal, null);
    Console.WriteLine("Content (re)cached");
}

Az alábbi kód egy teljes, futtatható teszt, ami a demonstrálja a direkt fájlolvasás (implicit OS cache), és a kézi cache-elés közötti teljesítménykülönbséget:


using System;
using System.Diagnostics;
using System.IO;
using System.Runtime.InteropServices;
using System.Threading;
using System.Web;
using System.Web.Caching;

class Program
{
    const string file = @"c:\temp\a.log";
    const string cacheKey = @"alog";
    const int loopLen = 50000;
    private static HttpRuntime httpRuntime;
    const int HRSharingViolation = -2147024864;

    static void Main(string[] args)
    {
        httpRuntime = new HttpRuntime();

        Thread t = new Thread(FileChanger);
        t.IsBackground = true;
        t.Start();

        int cc = GetSumGCCount();
        Stopwatch w = Stopwatch.StartNew();
        for (int i = 0; i < loopLen; i++)
        {
            TestWithoutCaching();
            ShowProgress(i);
        }
        w.Stop();
        Console.WriteLine("Elapsed time: {0}, collections during cached test: {1}",
            w.Elapsed, GetSumGCCount() - cc);

        cc = GetSumGCCount();
        w = Stopwatch.StartNew();
        for (int i = 0; i < loopLen; i++)
        {
            TestWithCaching();
            ShowProgress(i);
        }
        w.Stop();
        Console.WriteLine("Elapsed time: {0}, collections during noncached test: {1}",
            w.Elapsed, GetSumGCCount() - cc);
    }

    private static void ShowProgress(int i)
    {
        //Console.WriteLine(i);
        //if (i % (loopLen / 100) == 0) Console.WriteLine("{0}%", i * 100 / loopLen);
    }

    static void FileChanger(object state)
    {
        while (true)
        {
            string s = ProtectedRead(file);
            ProtectedWrite(file, s);

            Console.WriteLine("File has been modified sucessfully.");
            Thread.Sleep(100);
        }
    }

    private static int GetSumGCCount()
    {
        GC.Collect();
        GC.WaitForPendingFinalizers();

        int cc = 0;
        for (int gen = 0; gen < GC.MaxGeneration; ++gen)
        {
            cc += GC.CollectionCount(gen);
        }
        return cc;
    }

    private static void TestWithoutCaching()
    {
        string s = ProtectedRead(file);
        for (int i = 0; i < s.Length; i++)
        {
            char c = s[i];
        }
    }

    private static void TestWithCaching()
    {
        object o = HttpRuntime.Cache[cacheKey];
        if (o != null)
        {
            string s = (string)o;
            for (int i = 0; i < s.Length; i++)
            {
                char c = s[i];
            }
        }
        else
        {
            CacheDependency d = new CacheDependency(file);
            string s = ProtectedRead(file);
            HttpRuntime.Cache.Add(cacheKey, s, d,
                Cache.NoAbsoluteExpiration,
                Cache.NoSlidingExpiration, CacheItemPriority.Normal, null);
            Console.WriteLine("Content (re)cached");
        }
    }

    static string ProtectedRead(string file)
    {
        string s = null;
        bool retry = false;
        do
        {
            try
            {
                s = File.ReadAllText(file);
                break;
            }
            catch (IOException ex)
            {
                int hr = Marshal.GetHRForException(ex);
                if (hr == HRSharingViolation)
                {
                    retry = true;
                    Console.WriteLine("File is locked while reading, retrying...");
                    RandomSleep();
                }
                else
                {
                    throw;
                }
            }

        } while (retry);
        return s;
    }

    static void ProtectedWrite(string file, string content)
    {
        bool retry = false;
        do
        {
            try
            {
                File.WriteAllText(file, content);
                break;
            }
            catch (IOException ex)
            {
                int hr = Marshal.GetHRForException(ex);
                if (hr == HRSharingViolation)
                {
                    retry = true;
                    Console.WriteLine("File is locked while writing, retrying...");
                    RandomSleep();
                }
                else
                {
                    throw;
                }
            }

        } while (retry);
    }

    private static void RandomSleep()
    {
        int r = new Random().Next(30, 200);
        Thread.Sleep(r);
    }
}

Hogy realisztikus legyen a teszt egy külön szál írogatja is a kérdéses fájt, ami az egyszerűség kedvéért most egy lokális, 256ks állomány.
Az író-olvasó folyamatok persze időnként összeakadnak, ezért a hibakezelő és ismétlő logika a kódban. Bár elég sok mesterséges késleltetés van a példában, így is eléggé marakodik a két oldal. Nem csak az időket mérem a kódban, hanem a szemétgyűjtések számát is a két esetben. A direkt fájlolvasás esetén minden egyes olvasáskor újabb és újabb buffert allokálunk, ami persze jelentős memóriakényszert okoz. Habár lehetne mondjuk valami thread-local buffert előre allokálni és újrahasznosítani, a legtöbben úgyis ezt a kevésbé hatékony kódot használják, így realisztikus a teszt.
A cache-elős példa esetén egyszerűen csak kapunk egy referenciát a memóriában már benn levő fájltartalomra, így nem történik nagyméretű memóriafoglalás sem. Érdemes tudni, hogy a nagyobb memóriatartalmakat a CLR a Large Object Heap-en tárolja, amely kevésbé hatékony mint a sima heap, szóval érdemes ésszel élni vele.

Lássuk a program kimenetét, azaz az eredményeket:


File is locked while reading, retrying...
File has been modified sucessfully.
File has been modified sucessfully.
File is locked while writing, retrying...
File is locked while writing, retrying...
File is locked while writing, retrying...
File is locked while writing, retrying...
File is locked while reading, retrying...
File has been modified sucessfully.
File is locked while reading, retrying...
File has been modified sucessfully.
File is locked while writing, retrying...
File is locked while writing, retrying...
File is locked while writing, retrying...
File is locked while writing, retrying...
File is locked while writing, retrying...
Elapsed time: 00:00:01.2796481, collections during noncached test: 252
Content (re)cached
Elapsed time: 00:00:00.0826961, collections during cached test: 2

Látható, hogy a saját cache nagyon sokat gyorsít, ráadásul a memóriát is sokkal jobban kíméli. Valójában annyira gyors a cache-elős eset, hogy az író szálnak nincs is elég ideje módosítani a fájt, ami amiatt fontos, hogy lássuk, működik-e a cache invalidálás, azaz érzékeli-e a cache, hogy módosult a fájl. Ha az iterációk számát felvesszük a példában 5000-re, akkor már lesznek olyan esetek, amikor a CacheDependency észreveszi a fájváltozást:


File has been modified sucessfully.
Content (re)cached
File has been modified sucessfully.
Content (re)cached
File has been modified sucessfully.
Content (re)cached
File has been modified sucessfully.
File is locked while reading, retrying...
File has been modified sucessfully.
Content (re)cached
Content (re)cached
File has been modified sucessfully.
File is locked while reading, retrying...
File has been modified sucessfully.
Content (re)cached
Content (re)cached
Elapsed time: 00:00:00.7075406, collections during cached test: 5

Remélem tanulságos volt, de ha benéztem valamit, és nem érvényes a teszt, szóljatok, javítom.

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

Monday, February 23rd, 2009

A Connection Poolról még pár gondolat. Jó, hogy van ez a pool, meg gyorsít is, örülünk neki, főleg webalkalmazásokban. Olyan appokban viszont, ahol állandóan futnak a dolgaink, mint egy asztali alkalmazás esetén, nem biztos, hogy érdemes mohón nyitni-zárni a kapcsolatot.
A tőzsdei kereskedési algoritmusaim backtestje során sok százezer paraméterkombinációt néz végig a gép, próbálja meghatározni a legvalószínűbb nyerési esélyűt vagy legnagyobb profit/szórással rendelkezőt (ennél jóval bonyolultabb a dolog, de ez most nem tőzsdés bejegyzés lesz).
A DAL-t úgy írtam meg ahogy webalkalmazásokban megszoktam, így minden egyes trade mentésénél nyitottam-zártam a connectiont. Profilerrel megnézve kiderült, hogy a pool ellenére is a futási idő harmada az open/close-zal ment el. Emiatt készítettem kétféle SqlHelper osztályt, az egyik állandóan nyitott kapcsolattal működik, a másik az egyéb helyekre nyit-zár mind eddig.
Összegezve, a connection pool kiváló dolog pillanatokra futó alkalmazásokhoz, mint a webalkalmazások, de nem érdemes erőltetni, ha több százezerszer kell nyitni-zárni a kapcsolatot.