Zmogljivost v .NET Core

Zmogljivost v .NET Core

Zmogljivost v .NET Core

Pozdravljeni vsi skupaj! Ta članek je zbirka najboljših praks, ki jih moji kolegi in jaz že dolgo uporabljamo pri delu na različnih projektih.

Podatki o stroju, na katerem so bili opravljeni izračuni:BenchmarkDotNet=v0.11.5, OS=Windows 10.0.18362
Intel Core i5–8250U CPU 1.60 GHz (Kaby Lake R), 1 CPU, 8 logičnih in 4 fizična jedra
.NET Core SDK=3.0.100
[Host]: .NET Core 2.2.7 (CoreCLR 4.6.28008.02, CoreFX 4.6.28008.03), 64-bitni RyuJIT
Jedro: .NET Core 2.2.7 (CoreCLR 4.6.28008.02, CoreFX 4.6.28008.03), 64-bitni RyuJIT
[Host]: .NET Core 3.0.0 (CoreCLR 4.700.19.46205, CoreFX 4.700.19.46214), 64-bitni RyuJIT
Jedro: .NET Core 3.0.0 (CoreCLR 4.700.19.46205, CoreFX 4.700.19.46214), 64-bitni RyuJIT

Job=Core Runtime=Core

ToList proti ToArray in Cycles


Te informacije sem nameraval pripraviti z izdajo .NET Core 3.0, vendar so me prehiteli, nočem ukrasti slave nekoga drugega in kopirati informacij drugih ljudi, zato bom samo poudaril povezava do dobrega članka, kjer je primerjava podrobno opisana.

V svojem imenu vam želim samo predstaviti svoje meritve in rezultate, dodal sem jim vzvratne zanke za ljubitelje “C++ stila” pisanja zank.

Koda:

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

Hitrosti delovanja v .NET Core 2.2 in 3.0 so skoraj enake. Tukaj je tisto, kar mi je uspelo dobiti v .NET Core 3.0:

Zmogljivost v .NET Core

Zmogljivost v .NET Core

Sklepamo lahko, da je iterativna obdelava zbirke Array hitrejša zaradi notranjih optimizacij in eksplicitne dodelitve velikosti zbirke. Prav tako si velja zapomniti, da ima zbirka List svoje prednosti in da morate uporabiti pravo zbirko glede na zahtevane izračune. Tudi če pišete logiko za delo z zankami, ne pozabite, da je to navadna zanka in je tudi predmet morebitne optimizacije zanke. Na habru je bil pred časom objavljen članek: https://habr.com/ru/post/124910/. Še vedno je aktualna in priporočljiva za branje.

Vrzi

Pred enim letom sem delal v podjetju na podedovanem projektu, v tem projektu je bilo normalno obdelati validacijo polja prek konstrukcije poskusi-ulovi-vrzi. Že takrat sem razumel, da je to nezdrava poslovna logika za projekt, zato sem se, kadar je bilo mogoče, trudil, da takšnega dizajna ne bi uporabljal. Toda ugotovimo, zakaj je pristop k obravnavanju napak s tako konstrukcijo slab. Napisal sem majhno kodo za primerjavo obeh pristopov in naredil merila uspešnosti za vsako možnost.

Koda:

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

Rezultati v .NET Core 3.0 in Core 2.2 imajo podoben rezultat (.NET Core 3.0):

Zmogljivost v .NET Core

Zmogljivost v .NET Core

S poskusom catch je koda težje razumljiva in podaljša čas izvajanja vašega programa. Toda če potrebujete to konstrukcijo, ne smete vstavljati tistih vrstic kode, za katere se ne pričakuje, da bodo obravnavale napake - tako bo koda lažje razumljiva. Pravzaprav sistem ne obremenjuje toliko obravnavanje izjem, temveč vržanje samih napak prek konstrukta vrzi novo izjemo.

Vrženje izjem je počasnejše od nekega razreda, ki bo zbral napako v zahtevanem formatu. Če obdelujete obrazec ali podatke in jasno veste, kakšna bi morala biti napaka, zakaj je ne bi obdelali?

Ne bi smeli pisati konstrukta throw new Exception(), če ta situacija ni izjemna. Obravnava in metanje izjeme je zelo drago!!!

ToLower, ToLowerInvariant, ToUpper, ToUpperInvariant

V svojih 5 letih izkušenj z delom na platformi .NET sem naletel na številne projekte, ki so uporabljali ujemanje nizov. Videl sem tudi naslednjo sliko: obstajala je ena rešitev Enterprise s številnimi projekti, od katerih je vsak drugače izvajal primerjave nizov. Kaj pa uporabiti in kako to poenotiti? V knjigi CLR via C# avtorja Richterja sem prebral informacijo, da je metoda ToUpperInvariant() hitrejša od ToLowerInvariant().

Odlomek iz knjige:

Zmogljivost v .NET Core

Seveda nisem verjel in sem se odločil izvesti nekaj testov na ogrodju .NET Framework in rezultat me je šokiral - več kot 15-odstotno povečanje zmogljivosti. Nato sem naslednje jutro ob prihodu v službo te meritve pokazal nadrejenim in jim omogočil dostop do izvorne kode. Po tem sta bila 2 od 14 projektov spremenjena za prilagoditev novim meritvam in glede na to, da sta ta dva projekta obstajala za obdelavo ogromnih Excelovih tabel, je bil rezultat za izdelek več kot pomemben.

Predstavljam vam tudi mere za različne različice .NET Core, da se lahko vsak izmed vas odloči za najbolj optimalno rešitev. Rad bi samo dodal, da v podjetju, kjer delam, uporabljamo ToUpper() za primerjavo nizov.

Koda:

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

Zmogljivost v .NET Core

Zmogljivost v .NET Core

V .NET Core 3.0 je povečanje za vsako od teh metod ~x2 in uravnoteži implementacije med seboj.

Zmogljivost v .NET Core

Zmogljivost v .NET Core

Kompilacija stopnje

V zadnjem članku sem na kratko opisal to funkcionalnost, svoje besede bi rad popravil in dopolnil. Večnivojsko prevajanje pospeši čas zagona vaše rešitve, vendar žrtvujete, da bodo deli vaše kode v ozadju prevedeni v bolj optimizirano različico, kar lahko povzroči majhne stroške. S prihodom NET Core 3.0 se je čas gradnje za projekte z omogočenim nivojskim prevajanjem zmanjšal in napake, povezane s to tehnologijo, so bile odpravljene. Prej je ta tehnologija povzročila napake pri prvih zahtevah v ASP.NET Core in zamrznila med prvo gradnjo v načinu večnivojskega prevajanja. Trenutno je privzeto omogočen v .NET Core 3.0, vendar ga lahko onemogočite, če želite. Če ste na položaju team-lead, senior, middle ali ste vodja oddelka, potem morate razumeti, da hiter razvoj projekta povečuje vrednost ekipe in ta tehnologija vam bo omogočila, da prihranite čas tako razvijalcem. in čas samega projekta.

.NET raven višje

Nadgradite svojo različico .NET Framework / .NET Core. Pogosto vsaka nova različica zagotavlja dodatno izboljšanje zmogljivosti in dodaja nove funkcije.

Toda kaj točno so koristi? Oglejmo si nekaj izmed njih:

  • .NET Core 3.0 je predstavil slike R2R, ki bodo skrajšale čas zagona aplikacij .NET Core.
  • Z različico 2.2 se je pojavila Tier Compilation, zahvaljujoč kateri bodo programerji porabili manj časa za zagon projekta.
  • Podpora za nove standarde .NET.
  • Podpora za novo različico programskega jezika.
  • Optimizacija, z vsako novo različico se izboljša optimizacija osnovnih knjižnic Collection/Struct/Stream/String/Regex in še veliko več. Če se selite z ogrodja .NET Framework na .NET Core, boste takoj dobili velik dvig zmogljivosti. Kot primer prilagam povezavo do nekaterih optimizacij, ki so bile dodane v .NET Core 3.0: https://devblogs.microsoft.com/dotnet/performance-improvements-in-net-core-3-0/

Zmogljivost v .NET Core

Zaključek

Pri pisanju kode je vredno biti pozoren na različne vidike vašega projekta in uporabiti funkcije vašega programskega jezika in platforme, da dosežete najboljši rezultat. Vesel bom, če delite svoje znanje o optimizaciji v .NET.

Povezava do github

Vir: www.habr.com

Dodaj komentar