Výkon v .NET Core

Výkon v .NET Core

Výkon v .NET Core

Ahojte všetci! Tento článok je zbierkou osvedčených postupov, ktoré spolu s kolegami používame už dlho pri práci na rôznych projektoch.

Informácie o stroji, na ktorom boli vykonané výpočty:BenchmarkDotNet=v0.11.5, OS=Windows 10.0.18362
Intel Core i5-8250U CPU 1.60 GHz (Kaby Lake R), 1 CPU, 8 logických a 4 fyzické jadrá
.NET Core SDK=3.0.100
[Hostiteľ]: .NET Core 2.2.7 (CoreCLR 4.6.28008.02, CoreFX 4.6.28008.03), 64-bitový RyuJIT
Jadro: .NET Core 2.2.7 (CoreCLR 4.6.28008.02, CoreFX 4.6.28008.03), 64-bitový RyuJIT
[Hostiteľ]: .NET Core 3.0.0 (CoreCLR 4.700.19.46205, CoreFX 4.700.19.46214), 64-bitový RyuJIT
Jadro: .NET Core 3.0.0 (CoreCLR 4.700.19.46205, CoreFX 4.700.19.46214), 64-bitový RyuJIT

Job=Core Runtime=Core

ToList vs ToArray a Cycles


Plánoval som pripraviť tieto informácie s vydaním .NET Core 3.0, ale predbehli ma, nechcem kradnúť niekomu inú slávu a kopírovať informácie iných ľudí, takže len upozorním odkaz na dobrý článok, kde je porovnanie podrobne popísané.

Vo svojom mene vám chcem len predstaviť moje merania a výsledky, pridal som k nim spätné slučky pre milovníkov „C++ štýlu“ písania slučiek.

kód:

public class Bench
    {
        private List<int> _list;
        private int[] _array;

        [Params(100000, 10000000)] public int N;

        [GlobalSetup]
        public void Setup()
        {
            const int MIN = 1;
            const int MAX = 10;
            Random random = new Random();
            _list = Enumerable.Repeat(0, N).Select(i => random.Next(MIN, MAX)).ToList();
            _array = _list.ToArray();
        }

        [Benchmark]
        public int ForList()
        {
            int total = 0;
            for (int i = 0; i < _list.Count; i++)
            {
                total += _list[i];
            }

            return total;
        }
        
        [Benchmark]
        public int ForListFromEnd()
        {
            int total = 0;t
            for (int i = _list.Count-1; i > 0; i--)
            {
                total += _list[i];
            }

            return total;
        }

        [Benchmark]
        public int ForeachList()
        {
            int total = 0;
            foreach (int i in _list)
            {
                total += i;
            }

            return total;
        }

        [Benchmark]
        public int ForeachArray()
        {
            int total = 0;
            foreach (int i in _array)
            {
                total += i;
            }

            return total;
        }

        [Benchmark]
        public int ForArray()
        {
            int total = 0;
            for (int i = 0; i < _array.Length; i++)
            {
                total += _array[i];
            }

            return total;
        }
        
        [Benchmark]
        public int ForArrayFromEnd()
        {
            int total = 0;
            for (int i = _array.Length-1; i > 0; i--)
            {
                total += _array[i];
            }

            return total;
        }
    }

Rýchlosti výkonu v .NET Core 2.2 a 3.0 sú takmer totožné. Tu je to, čo sa mi podarilo získať v .NET Core 3.0:

Výkon v .NET Core

Výkon v .NET Core

Môžeme konštatovať, že iteratívne spracovanie kolekcie Array je rýchlejšie vďaka jej interným optimalizáciám a explicitnému prideleniu veľkosti kolekcie. Je tiež potrebné pripomenúť, že kolekcia zoznamu má svoje výhody a mali by ste použiť správnu kolekciu v závislosti od požadovaných výpočtov. Aj keď píšete logiku pre prácu so slučkami, nezabudnite, že ide o obyčajnú slučku a podlieha aj prípadnej optimalizácii slučky. Na habr bol už dávno publikovaný článok: https://habr.com/ru/post/124910/. Je to stále relevantné a odporúčané čítanie.

Hádzať

Pred rokom som pracoval v jednej spoločnosti na starom projekte, v tomto projekte bolo normálne spracovať validáciu v teréne pomocou konštruktu vyskúšaj-chytni-hoď. Už vtedy som pochopil, že to bola nezdravá obchodná logika projektu, takže vždy, keď to bolo možné, som sa snažil takýto dizajn nepoužívať. Poďme však zistiť, prečo je prístup k riešeniu chýb pri takejto konštrukcii zlý. Napísal som malý kód na porovnanie týchto dvoch prístupov a urobil som benchmarky pre každú možnosť.

kód:

        public bool ContainsHash()
        {
            bool result = false;
            foreach (var file in _files)
            {
                var extension = Path.GetExtension(file);
                if (_hash.Contains(extension))
                    result = true;
            }

            return result;
        }

        public bool ContainsHashTryCatch()
        {
            bool result = false;
            try
            {
                foreach (var file in _files)
                {
                    var extension = Path.GetExtension(file);
                    if (_hash.Contains(extension))
                        result = true;
                }
                
                if(!result) 
                    throw new Exception("false");
            }
            catch (Exception e)
            {
                result = false;
            }

            return result;
        }

Výsledky v .NET Core 3.0 a Core 2.2 majú podobný výsledok (.NET Core 3.0):

Výkon v .NET Core

Výkon v .NET Core

Skúste catch sťažuje pochopenie kódu a predlžuje čas vykonávania vášho programu. Ak však potrebujete tento dizajn, nemali by ste vkladať tie riadky kódu, od ktorých sa neočakáva, že budú spracovávať chyby – kód sa tým uľahčí na pochopenie. V skutočnosti to nie je ani tak spracovanie výnimiek, čo zaťažuje systém, ale skôr vyhadzovanie samotných chýb prostredníctvom novej konštrukcie výnimiek.

Vyhadzovanie výnimiek je pomalšie ako niektoré triedy, ktoré zhromaždia chybu v požadovanom formáte. Ak spracovávate formulár alebo nejaké údaje a jasne viete, v čom by mala byť chyba, prečo to nespracovať?

Nemali by ste písať novú konštrukciu Exception(), ak táto situácia nie je výnimočná. Manipulácia a vyhodenie výnimky je veľmi drahé!!!

ToLower, ToLowerInvariant, ToUpper, ToUpperInvariant

Počas mojich 5-ročných skúseností s prácou na platforme .NET som sa stretol s mnohými projektmi, ktoré využívali porovnávanie reťazcov. Videl som aj nasledujúci obrázok: existovalo jedno Enterprise riešenie s mnohými projektmi, z ktorých každý vykonával porovnávanie reťazcov inak. Čo však treba použiť a ako to zjednotiť? V knihe CLR via C# od Richtera som sa dočítal informáciu, že metóda ToUpperInvariant() je rýchlejšia ako ToLowerInvariant().

Úryvok z knihy:

Výkon v .NET Core

Samozrejme, neveril som tomu a rozhodol som sa vykonať niekoľko testov na .NET Framework a výsledok ma šokoval - viac ako 15% nárast výkonu. Potom, keď som nasledujúce ráno prišiel do práce, ukázal som tieto merania svojim nadriadeným a umožnil som im prístup k zdrojovému kódu. Potom boli 2 zo 14 projektov zmenené tak, aby vyhovovali novým meraniam, a vzhľadom na to, že tieto dva projekty existovali na spracovanie obrovských excelových tabuliek, výsledok bol pre produkt viac než významný.

Predstavujem vám aj merania pre rôzne verzie .NET Core, aby si každý z vás mohol vybrať to najoptimálnejšie riešenie. A chcem len dodať, že vo firme, kde pracujem, používame na porovnávanie reťazcov ToUpper().

kód:

public const string defaultString =  "VXTDuob5YhummuDq1PPXOHE4PbrRjYfBjcHdFs8UcKSAHOCGievbUItWhU3ovCmRALgdZUG1CB0sQ4iMj8Z1ZfkML2owvfkOKxBCoFUAN4VLd4I8ietmlsS5PtdQEn6zEgy1uCVZXiXuubd0xM5ONVZBqDu6nOVq1GQloEjeRN8jXrj0MVUexB9aIECs7caKGddpuut3";

        [Benchmark]
        public bool ToLower()
        {
            return defaultString.ToLower() == defaultString.ToLower();
        }

        [Benchmark]
        public bool ToLowerInvariant()
        {
            return defaultString.ToLowerInvariant() == defaultString.ToLowerInvariant();
        }

        [Benchmark]
        public bool ToUpper()
        {
            return defaultString.ToUpper() == defaultString.ToUpper();
        }

        [Benchmark]
        public bool ToUpperInvariant()
        {
            return defaultString.ToUpperInvariant() == defaultString.ToUpperInvariant();
        }

Výkon v .NET Core

Výkon v .NET Core

V .NET Core 3.0 je nárast pre každú z týchto metód ~x2 a vyrovnáva medzi sebou implementácie.

Výkon v .NET Core

Výkon v .NET Core

Kompilácia vrstiev

V mojom minulom článku som túto funkcionalitu stručne opísal, rád by som svoje slová poopravil a doplnil. Viacúrovňová kompilácia urýchľuje čas spustenia vášho riešenia, ale obetujete, že časti vášho kódu budú skompilované do optimalizovanejšej verzie na pozadí, čo môže predstavovať malú réžiu. S príchodom NET Core 3.0 sa skrátil čas zostavovania projektov s povolenou kompiláciou vrstiev a boli opravené chyby spojené s touto technológiou. Predtým táto technológia viedla k chybám v prvých požiadavkách v ASP.NET Core a zamrzla počas prvého zostavenia v režime viacúrovňovej kompilácie. V súčasnosti je v .NET Core 3.0 štandardne povolená, ale ak chcete, môžete ju zakázať. Ak ste na pozícii team-leader, senior, middle, alebo ste vedúci oddelenia, potom musíte pochopiť, že rýchly rozvoj projektu zvyšuje hodnotu tímu a táto technológia vám umožní ušetriť čas pre oboch vývojárov. a čas samotného projektu.

.NET o úroveň vyššie

Inovujte svoju verziu .NET Framework / .NET Core. Každá nová verzia často poskytuje ďalšie zvýšenie výkonu a pridáva nové funkcie.

Ale aké sú konkrétne výhody? Pozrime sa na niektoré z nich:

  • .NET Core 3.0 predstavil obrazy R2R, ktoré skrátia čas spustenia aplikácií .NET Core.
  • S verziou 2.2 sa objavila Tier Compilation, vďaka ktorej programátori strávia menej času spúšťaním projektu.
  • Podpora pre nové štandardy .NET.
  • Podpora novej verzie programovacieho jazyka.
  • Optimalizácia, s každou novou verziou sa zlepšuje optimalizácia základných knižníc Collection/Struct/Stream/String/Regex a mnoho ďalších. Ak migrujete z .NET Framework na .NET Core, získate veľké zvýšenie výkonu hneď po vybalení. Ako príklad prikladám odkaz na niektoré optimalizácie, ktoré boli pridané do .NET Core 3.0: https://devblogs.microsoft.com/dotnet/performance-improvements-in-net-core-3-0/

Výkon v .NET Core

Záver

Pri písaní kódu sa oplatí venovať pozornosť rôznym aspektom vášho projektu a využívať funkcie vášho programovacieho jazyka a platformy, aby ste dosiahli čo najlepší výsledok. Budem rád, ak sa podelíte o svoje poznatky súvisiace s optimalizáciou v .NET.

Odkaz na github

Zdroj: hab.com

Pridať komentár