Výkon v .NET Core

Výkon v .NET Core

Výkon v .NET Core

Ahoj všichni! Tento článek je sbírkou osvědčených postupů, které s kolegy používáme již dlouhou dobu při práci na různých projektech.

Informace o stroji, na kterém byly výpočty provedeny: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á jádra
.NET Core SDK=3.0.100
[Hostitel]: .NET Core 2.2.7 (CoreCLR 4.6.28008.02, CoreFX 4.6.28008.03), 64bitový RyuJIT
Jádro: .NET Core 2.2.7 (CoreCLR 4.6.28008.02, CoreFX 4.6.28008.03), 64bitový RyuJIT
[Hostitel]: .NET Core 3.0.0 (CoreCLR 4.700.19.46205, CoreFX 4.700.19.46214), 64bitový RyuJIT
Jádro: .NET Core 3.0.0 (CoreCLR 4.700.19.46205, CoreFX 4.700.19.46214), 64bitový RyuJIT

Job=Core Runtime=Core

ToList vs ToArray a Cycles


Plánoval jsem připravit tyto informace s vydáním .NET Core 3.0, ale předběhli mě, nechci krást slávu někoho jiného a kopírovat informace jiných lidí, takže jen podotknu odkaz na dobrý článek, kde je srovnání podrobně popsáno.

Za sebe vám chci jen představit svá měření a výsledky, přidal jsem k nim zpětné smyčky pro milovníky „C++ stylu“ psaní smyček.

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

Rychlosti výkonu v .NET Core 2.2 a 3.0 jsou téměř totožné. Zde je to, co se mi podařilo získat v .NET Core 3.0:

Výkon v .NET Core

Výkon v .NET Core

Můžeme dojít k závěru, že iterativní zpracování kolekce Array je rychlejší díky její vnitřní optimalizaci a explicitní alokaci velikosti kolekce. Rovněž stojí za to připomenout, že kolekce List má své výhody a měli byste použít správnou kolekci v závislosti na požadovaných výpočtech. I když píšete logiku pro práci se smyčkami, nezapomeňte, že se jedná o obyčejnou smyčku a také podléhá případné optimalizaci smyčky. Článek na habr vyšel už docela dávno: https://habr.com/ru/post/124910/. Je to stále aktuální a doporučená četba.

Házet

Před rokem jsem pracoval ve společnosti na starším projektu, v tomto projektu bylo normální zpracovávat validaci v terénu pomocí konstruktu try-catch-throw. Už tehdy jsem pochopil, že to byla nezdravá obchodní logika projektu, a tak jsem se vždy, když to bylo možné, snažil takový návrh nepoužívat. Ale pojďme zjistit, proč je přístup k řešení chyb s takovou konstrukcí špatný. Napsal jsem malý kód pro porovnání těchto dvou přístupů a provedl jsem srovnávací testy pro každou možnost.

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

Výkon v .NET Core

Výkon v .NET Core

Try catch znesnadňuje pochopení kódu a prodlužuje dobu provádění vašeho programu. Pokud však tuto konstrukci potřebujete, neměli byste vkládat ty řádky kódu, u kterých se neočekává, že budou zpracovávat chyby – kód bude srozumitelnější. Ve skutečnosti to není ani tak zpracování výjimek, co zatěžuje systém, jako spíše házení samotných chyb prostřednictvím konstrukce throw new Exception.

Vyhazování výjimek je pomalejší než nějaká třída, která shromáždí chybu v požadovaném formátu. Pokud zpracováváte formulář nebo nějaká data a jasně víte, v čem by měla být chyba, proč to nezpracovat?

Pokud tato situace není výjimečná, neměli byste psát novou konstrukci Exception(). Manipulace a vyhození výjimky je velmi drahé!!!

ToLower, ToLowerInvariant, ToUpper, ToUpperInvariant

Za svých 5 let zkušeností s prací na platformě .NET jsem narazil na mnoho projektů, které využívaly párování řetězců. Také jsem viděl následující obrázek: existovalo jedno Enterprise řešení s mnoha projekty, z nichž každý prováděl porovnávání řetězců jinak. Co ale použít a jak to sjednotit? V knize CLR via C# od Richtera jsem četl informaci, že metoda ToUpperInvariant() je rychlejší než ToLowerInvariant().

Úryvek z knihy:

Výkon v .NET Core

Samozřejmě jsem tomu nevěřil a rozhodl jsem se provést několik testů na .NET Framework a výsledek mě šokoval - více než 15% nárůst výkonu. Potom, když jsem druhý den ráno dorazil do práce, ukázal jsem tato měření svým nadřízeným a dal jim přístup ke zdrojovému kódu. Poté byly 2 ze 14 projektů změněny tak, aby vyhovovaly novým měřením, a vzhledem k tomu, že tyto dva projekty existovaly pro zpracování obrovských excelových tabulek, byl výsledek pro produkt více než významný.

Představuji vám také měření pro různé verze .NET Core, aby si každý z vás mohl vybrat to nejoptimálnější řešení. A chci jen dodat, že ve firmě, kde pracuji, používáme ToUpper() k porovnávání řetězců.

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árůst pro každou z těchto metod ~x2 a vyrovnává implementace mezi sebou.

Výkon v .NET Core

Výkon v .NET Core

Kompilace vrstev

Ve svém minulém článku jsem tuto funkcionalitu stručně popsal, rád bych svá slova opravil a doplnil. Víceúrovňová kompilace urychluje dobu spouštění vašeho řešení, ale obětujete, že části vašeho kódu budou na pozadí zkompilovány do optimalizovanější verze, což může představovat malou režii. S příchodem NET Core 3.0 se doba sestavování projektů s povolenou kompilací vrstev zkrátila a byly opraveny chyby spojené s touto technologií. Dříve tato technologie vedla k chybám v prvních požadavcích v ASP.NET Core a zamrzla během prvního sestavení v režimu víceúrovňové kompilace. V současnosti je v .NET Core 3.0 ve výchozím nastavení povolena, ale pokud si přejete, můžete ji zakázat. Pokud jste na pozici teamleader, senior, middle, nebo jste vedoucí oddělení, pak musíte pochopit, že rychlý vývoj projektu zvyšuje hodnotu týmu a tato technologie vám umožní ušetřit čas oběma vývojářům a čas samotného projektu.

.NET o úroveň výš

Upgradujte svou verzi .NET Framework / .NET Core. Každá nová verze často poskytuje další zvýšení výkonu a přidává nové funkce.

Ale jaké jsou vlastně výhody? Podívejme se na některé z nich:

  • .NET Core 3.0 představil obrazy R2R, které zkrátí dobu spouštění aplikací .NET Core.
  • S verzí 2.2 se objevila Tier Compilation, díky které programátoři stráví méně času spouštěním projektu.
  • Podpora nových standardů .NET.
  • Podpora nové verze programovacího jazyka.
  • Optimalizace, s každou novou verzí se zlepšuje optimalizace základních knihoven Collection/Struct/Stream/String/Regex a mnohem více. Pokud přecházíte z .NET Framework na .NET Core, získáte hned po vybalení velké zvýšení výkonu. Jako příklad přikládám odkaz na některé optimalizace, které byly přidány do .NET Core 3.0: https://devblogs.microsoft.com/dotnet/performance-improvements-in-net-core-3-0/

Výkon v .NET Core

Závěr

Při psaní kódu se vyplatí věnovat pozornost různým aspektům vašeho projektu a využívat funkce vašeho programovacího jazyka a platformy k dosažení nejlepšího výsledku. Budu rád, když se podělíte o své znalosti týkající se optimalizace v .NET.

Odkaz na github

Zdroj: www.habr.com

Přidat komentář