Performanse u .NET Core

Performanse u .NET Core

Performanse u .NET Core

Zdravo svima! Ovaj članak je zbirka najboljih praksi koje moje kolege i ja već dugo koristimo pri radu na različitim projektima.

Podaci o mašini na kojoj su izvršeni proračuni:BenchmarkDotNet=v0.11.5, OS=Windows 10.0.18362
Intel Core i5-8250U CPU 1.60GHz (Kaby Lake R), 1 CPU, 8 logičkih i 4 fizička jezgra
.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
Jezgra: .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
Jezgra: .NET Core 3.0.0 (CoreCLR 4.700.19.46205, CoreFX 4.700.19.46214), 64-bitni RyuJIT

Job=Core Runtime=Jezgro

ToList vs ToArray i Cycles


Planirao sam pripremiti ovu informaciju sa izdavanjem .NET Core 3.0, ali su me pobijedili, ne želim da kradem tuđu slavu i kopiram tuđe informacije, pa ću samo istaći link do dobrog članka gdje je poređenje detaljno opisano.

U svoje ime samo želim da vam predstavim svoja mjerenja i rezultate; dodao sam im obrnute petlje za ljubitelje “C++ stila” pisanja petlji.

Kod:

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

Brzine performansi u .NET Core 2.2 i 3.0 su skoro identične. Evo šta sam uspeo da dobijem u .NET Core 3.0:

Performanse u .NET Core

Performanse u .NET Core

Možemo zaključiti da je iterativna obrada zbirke Array brža zbog njenih internih optimizacija i eksplicitne alokacije veličine kolekcije. Također je vrijedno zapamtiti da kolekcija List ima svoje prednosti i trebali biste koristiti pravu kolekciju ovisno o potrebnim proračunima. Čak i ako pišete logiku za rad sa petljama, ne zaboravite da je ovo obična petlja i da je također podložna mogućoj optimizaciji petlje. Na habru je davno objavljen članak: https://habr.com/ru/post/124910/. I dalje je relevantna i preporučena literatura.

Baci

Prije godinu dana radio sam u jednoj kompaniji na naslijeđenom projektu, u tom projektu je bilo normalno da se validacija na terenu obrađuje kroz konstrukciju pokušaj-hvataj-baci. Već sam tada shvatio da je to nezdrava poslovna logika projekta, pa sam kad god je bilo moguće nastojao da ne koristim takav dizajn. Ali hajde da shvatimo zašto je pristup rukovanju greškama sa takvom konstrukcijom loš. Napisao sam mali kod da uporedim dva pristupa i napravio benchmark za svaku opciju.

Kod:

        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 u .NET Core 3.0 i Core 2.2 imaju sličan rezultat (.NET Core 3.0):

Performanse u .NET Core

Performanse u .NET Core

Try catch čini kod težim za razumijevanje i povećava vrijeme izvršavanja vašeg programa. Ali ako vam je potrebna ova konstrukcija, ne biste trebali umetati one linije koda za koje se ne očekuje da će rješavati greške - to će kod lakše razumjeti. U stvari, nije toliko rukovanje izuzecima ono što učitava sistem, već bacanje samih grešaka kroz konstrukciju throw new Exception.

Izbacivanje izuzetaka je sporije od neke klase koja će prikupiti grešku u traženom formatu. Ako obrađujete obrazac ili neke podatke i jasno znate koja bi greška trebala biti, zašto je ne obraditi?

Ne biste trebali napisati konstrukciju throw new Exception() ako ova situacija nije izuzetna. Rukovanje i izbacivanje izuzetka je veoma skupo!!!

ToLower, ToLowerInvarijant, ToUpper, ToUpperInvarijant

Tokom mojih 5 godina iskustva rada na .NET platformi, naišao sam na mnoge projekte koji su koristili podudaranje nizova. Vidio sam i sljedeću sliku: postojalo je jedno Enterprise rješenje sa mnogo projekata, od kojih je svaki različito izvodio poređenja nizova. Ali šta treba koristiti i kako to ujediniti? U knjizi CLR preko C# od Richtera, pročitao sam informaciju da je metoda ToUpperInvariant() brža od ToLowerInvariant().

Odlomak iz knjige:

Performanse u .NET Core

Naravno, nisam vjerovao i odlučio sam tada izvršiti neke testove na .NET Frameworku i rezultat me šokirao - više od 15% povećanja performansi. Zatim, po dolasku na posao sljedećeg jutra, pokazao sam ova mjerenja svojim nadređenima i dao im pristup izvornom kodu. Nakon toga su 2 od 14 projekata promijenjena kako bi se prilagodila novim mjerenjima, a s obzirom da su ova dva projekta postojala za obradu ogromnih Excel tabela, rezultat je bio više nego značajan za proizvod.

Predstavljam vam i mjerenja za različite verzije .NET Core-a, tako da svako od vas može izabrati najoptimalnije rješenje. I samo želim da dodam da u kompaniji u kojoj radim koristimo ToUpper() za upoređivanje stringova.

Kod:

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

Performanse u .NET Core

Performanse u .NET Core

U .NET Core 3.0, povećanje za svaku od ovih metoda je ~x2 i balansira implementacije među sobom.

Performanse u .NET Core

Performanse u .NET Core

Tier Compilation

U svom posljednjem članku sam ukratko opisao ovu funkcionalnost, želio bih ispraviti i dopuniti svoje riječi. Kompilacija na više nivoa ubrzava vrijeme pokretanja vašeg rješenja, ali žrtvujete da će dijelovi vašeg koda biti prevedeni u optimiziraniju verziju u pozadini, što može dovesti do malih troškova. S pojavom NET Core 3.0, vrijeme izgradnje za projekte s omogućenom kompilacijom nivoa se smanjilo i ispravljene su greške povezane s ovom tehnologijom. Ranije je ova tehnologija dovodila do grešaka u prvim zahtjevima u ASP.NET Core i zamrzavala se tokom prve izgradnje u višerazinskom načinu kompilacije. Trenutno je podrazumevano omogućen u .NET Core 3.0, ali ga možete onemogućiti ako želite. Ako ste na poziciji voditelja tima, seniora, srednjeg ili ste šef odjela, onda morate shvatiti da brz razvoj projekta povećava vrijednost tima i ova tehnologija će vam omogućiti da uštedite vrijeme za oba programera i vrijeme samog projekta.

.NET nivo gore

Nadogradite svoju .NET Framework / .NET Core verziju. Često, svaka nova verzija pruža dodatna poboljšanja performansi i dodaje nove funkcije.

Ali koje su tačno prednosti? Pogledajmo neke od njih:

  • .NET Core 3.0 je predstavio R2R slike koje će smanjiti vrijeme pokretanja .NET Core aplikacija.
  • Sa verzijom 2.2 pojavila se Tier Compilation, zahvaljujući kojoj će programeri trošiti manje vremena na pokretanje projekta.
  • Podrška za nove .NET standarde.
  • Podrška za novu verziju programskog jezika.
  • Optimizacija, sa svakom novom verzijom poboljšava se optimizacija osnovnih biblioteka Collection/Struct/Stream/String/Regex i još mnogo toga. Ako prelazite sa .NET Framework na .NET Core, dobićete veliko povećanje performansi iz kutije. Kao primjer, prilažem vezu na neke od optimizacija koje su dodate u .NET Core 3.0: https://devblogs.microsoft.com/dotnet/performance-improvements-in-net-core-3-0/

Performanse u .NET Core

zaključak

Prilikom pisanja koda, vrijedno je obratiti pažnju na različite aspekte vašeg projekta i koristiti karakteristike vašeg programskog jezika i platforme kako biste postigli najbolji rezultat. Bilo bi mi drago da podijelite svoje znanje o optimizaciji u .NET-u.

Link na github

izvor: www.habr.com

Dodajte komentar