Performance in .NET Core

Performance in .NET Core

Performance in .NET Core

Ahojte! Tento článok je zbierkou najlepších praktik, ktoré používame ja a moji kolegovia už dlhú dobu pri práci na rôznych projektoch.

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

Job=Core Runtime=Core

ToList vs ToArray a Cycles


Túto informáciu som plánoval pripraviť s vydaním .NET Core 3.0, ale predbehli ma, nechcem kradnúť cudziu slávu a kopírovať cudzie informácie, preto len uvediem odkaz na dobrý článok, kde je podrobne rozpísané porovnanie.

Od seba chcem len predstaviť svoje merania a výsledky, do ktorých som pridal spätné cykly pre milovníkov „C++ štýlu“ písania cyklov.

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ýchlosť práce v .NET Core 2.2 a 3.0 je takmer identická. Toto sú výsledky, ktoré sa mi podarilo získať v .NET Core 3.0:

Performance in .NET Core

Performance in .NET Core

Môžeme dospieť k záveru, že cyklické spracovanie kolekcie typu Array je rýchlejšie vďaka svojim vnútorným optimalizáciám a jasnému určenému veľkosti kolekcie. Taktiež je dôležité pamätať, že kolekcia typu List má svoje výhody a mali by ste použiť potrebnú kolekciu v závislosti od požadovaných výpočtov. Aj keď píšete logiku práce s cyklami, nezabúdajte, že ide o obyčajný loop, ktorý je taktiež náchylný na možné optimalizácie cyklov. Na habr pred časom vyšiel článok: https://habr.com/ru/post/124910/. Ten je stále aktuálny a odporúča sa prečítať.

Throw

Pred rokom som pracoval v spoločnosti na legacy projekte, kde bolo v rámci normy spracovávať validáciu polí prostredníctvom konštrukcie try-catch-throw. Už vtedy som chápal, že to je nezdravá firemná logika fungovania projektu, a preto som sa snažil takúto konštrukciu vyhýbať. Ale poďme sa pozrieť, čím je prístup spracovávať chyby touto konštrukciou zlý. Napísal som malý kód na porovnanie dvoch prístupov a zmeral „benchmarks“ pre každú variantu.

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ýsledky (.NET Core 3.0):

Performance in .NET Core

Performance in .NET Core

Try catch komplikuje pochopenie kódu a zvyšuje čas vykonávania vašej aplikácie. Ale ak túto konštrukciu potrebujete, nemali by ste vkladať tie riadky kódu, od ktorých sa neočakáva spracovanie chýb — to uľahčí pochopenie kódu. V skutočnosti nezaťažuje systém tak spracovanie výnimiek, ako skôr vyhadzovanie samotných chýb prostredníctvom konštrukcie throw new Exception.

Vyhadzovanie výnimiek je pomalšie ako akákoľvek trieda, ktorá zberie chybu v požadovanom formáte. Ak spracovávate formulár alebo akékoľvek údaje a jasne viete, aká chyba by mala byť, prečo ich potom nespracovať?

Nemali by ste používať konštrukciu throw new Exception(), ak táto situácia nie je výnimočná. Spracovanie a vyhadzovanie výnimiek je veľmi nákladné!!!

ToLower, ToLowerInvariant, ToUpper, ToUpperInvariant

Za svojich 5 rokov praxe na platforme .NET som sa stretol s mnohými projektmi, ktoré používali porovnávanie reťazcov. Taktiež som videl nasledujúci obrázok: existovalo jedno Enterprise riešenie s množstvom projektov, z ktorých každý vykonával porovnávanie reťazcov inak. Ale čo by sme mali použiť a ako to unifikovať? V knihe CLR via C# od Rychtera som sa dočítal, že metóda ToUpperInvariant() je rýchlejšia ako ToLowerInvariant().

Úryvok z knihy:

Performance in .NET Core

Samozrejme, že som tomu neveril a rozhodol som sa vykonať niektoré testy na .NET Framework a výsledok ma šokoval — viac ako 15% zisk výkonu. Nasledujúce ráno po príchode do práce som ukázal tieto merania svojmu nadriadenému a poskytol mu prístup k zdrojovým kódom. Po tom 2 z 14 projektov boli upravené podľa nových meraní, a keďže tieto dva projekty spracovávali obrovské excelové tabuľky, výsledok bol pre produkt viac než významný.

Taktiež vám predstavujem merania pre rôzne verzie .NET Core, aby ste si každý mohol vybrať najoptimálnejšie riešenie. A len chcem doplniť, že v spoločnosti, kde pracujem, používame ToUpper() na porovnávanie reťazcov.

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

Performance in .NET Core

Performance in .NET Core

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

Performance in .NET Core

Performance in .NET Core

Tier Compilation

V predchádzajúcom článku som túto funkciu opísal stručne, chcel by som upraviť a doplniť svoje slová. Viacúrovňová kompilácia urýchľuje čas spustenia vášho riešenia, ale obetujete tým, že časti vášho kódu budú kompilované do optimalizovanej verzie na pozadí, čo môže viesť k drobným režijným nákladom. S príchodom .NET Core 3.0 sa skrátil čas zostavovania projektov s povolenou tier compilation a opravili sa chyby súvisiace s touto technológiou. Predtým táto technológia spôsobovala chyby pri prvých požiadavkách v ASP.NET Core a zamŕzanie pri prvom zostavení v režime viacúrovňovej kompilácie. V súčasnosti je v .NET Core 3.0 táto funkcia povolená predvolene, ale môžete ju podľa želania vypnúť. Ak ste na pozícii team-lead, senior, middle alebo vedúci oddelenia, mali by ste pochopiť, že rýchly vývoj projektu zvyšuje hodnotu tímu a táto technológia vám umožní ušetriť čas ako vývojárom, tak aj čas realizácie projektu.

.NET úroveň vyššie

Zvýšte verziu vášho .NET Framework / .NET Core. Často, každá nová verzia prináša dodatočný nárast výkonu a pridáva nové funkcie.

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

  • V .NET Core 3.0 sa objavili R2R obrazy, ktoré umožnia skrátiť čas spustenia aplikácií .NET Core.
  • Od verzie 2.2 sa objavila Tier Compilation, vďaka ktorej programátori strávia menej času spúšťaním projektu.
  • Podpora nových štandardov .NET Standard.
  • Podpora novej verzie programovacieho jazyka.
  • Optimalizácia, s každou novou verziou sa zlepšuje optimalizácia základnými knižnicami Collection/Struct/Stream/String/Regex a mnohými ďalšími. Ak prechádzate z .NET Framework na .NET Core, získate značný nárast výkonu už od začiatku. Pre ilustráciu prikladám odkaz na časť optimalizácií, ktoré boli pridané do .NET Core 3.0: https://devblogs.microsoft.com/dotnet/performance-improvements-in-net-core-3-0/

Performance in .NET Core

Záver

Pri písaní kódu stojí za to venovať pozornosť rôznym aspektom vášho projektu a využiť funkcie svojho programovacieho jazyka a platformy na dosiahnutie najlepších výsledkov. Budem rád, ak sa podelíte o svoje poznatky týkajúce sa optimalizácie v .NET.

Odkaz na github

Zdroj: habr.com

Kúpte si spoľahlivý hosting pre webové stránky s ochranou proti DDoS, VPS VDS servery 🔥 Kúpte si spoľahlivý hosting pre webové stránky s ochranou proti DDoS, VPS VDS servery | ProHoster