Rendimento en .NET Kerno

Rendimento en .NET Kerno

Rendimento en .NET Kerno

Saluton al ĉiuj! Ĉi tiu artikolo estas kolekto de Plej bonaj Praktikoj kiujn miaj kolegoj kaj mi uzas dum longa tempo kiam ili laboras pri malsamaj projektoj.

Informoj pri la maŝino sur kiu la kalkuloj estis faritaj:BenchmarkDotNet=v0.11.5, OS=Vindozo 10.0.18362
Intel Core i5-8250U CPU 1.60GHz (Kaby Lake R), 1 CPU, 8 logikaj kaj 4 fizikaj kernoj
.NET Core SDK=3.0.100
[Gastiganto]: .NET Kerno 2.2.7 (CoreCLR 4.6.28008.02, CoreFX 4.6.28008.03), 64bit RyuJIT
Kerno: .NET Kerno 2.2.7 (CoreCLR 4.6.28008.02, CoreFX 4.6.28008.03), 64bit RyuJIT
[Gastiganto]: .NET Kerno 3.0.0 (CoreCLR 4.700.19.46205, CoreFX 4.700.19.46214), 64bit RyuJIT
Kerno: .NET Kerno 3.0.0 (CoreCLR 4.700.19.46205, CoreFX 4.700.19.46214), 64bit RyuJIT

Job=Kerno Runtime=Kerno

ToList vs ToArray kaj Cikloj


Mi planis prepari ĉi tiujn informojn per la ĵeto de .NET Core 3.0, sed ili batis min al ĝi, mi ne volas ŝteli alies gloron kaj kopii informojn de aliaj homoj, do mi nur atentigos. ligo al bona artikolo kie la komparo estas detale priskribita.

En mia propra nomo, mi nur volas prezenti al vi miajn mezurojn kaj rezultojn; Mi aldonis al ili inversajn buklojn por amantoj de la "C++-stilo" de skribbukloj.

kodo

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

Efikecrapidecoj en .NET Core 2.2 kaj 3.0 estas preskaŭ identaj. Jen kion mi povis akiri en .NET Core 3.0:

Rendimento en .NET Kerno

Rendimento en .NET Kerno

Ni povas konkludi, ke ripeta pretigo de Array-kolekto estas pli rapida pro siaj internaj optimumigoj kaj eksplicita kolekto-granda atribuo. Ankaŭ indas memori, ke Listo-kolekto havas siajn proprajn avantaĝojn kaj vi devus uzi la ĝustan kolekton depende de la necesaj kalkuloj. Eĉ se vi skribas logikon por labori kun bukloj, ne forgesu, ke ĉi tio estas ordinara buklo kaj ĝi ankaŭ estas submetita al ebla buklooptimumigo. Artikolo estis publikigita sur habr antaŭ sufiĉe longa tempo: https://habr.com/ru/post/124910/. Ĝi ankoraŭ estas trafa kaj rekomendinda legado.

Ĵetu

Antaŭ jaro, mi laboris ĉe kompanio pri hereda projekto, en tiu projekto estis normale prilabori kampvalidigon per try-catch-throw konstrukcio. Mi jam komprenis tiam, ke tio estas nesana komerca logiko por la projekto, do kiam ajn eble mi provis ne uzi tian dezajnon. Sed ni eltrovu kial la aliro al pritraktado de eraroj kun tia konstruo estas malbona. Mi skribis malgrandan kodon por kompari la du alirojn kaj faris komparnormojn por ĉiu opcio.

kodo

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

La rezultoj en .NET Core 3.0 kaj Core 2.2 havas similan rezulton (.NET Core 3.0):

Rendimento en .NET Kerno

Rendimento en .NET Kerno

Provu catch faras la kodon pli malfacile komprenebla kaj pliigas la ekzekuttempon de via programo. Sed se vi bezonas ĉi tiun konstruon, vi ne enmetu tiujn liniojn de kodo, kiuj ne atendas erarojn - tio plifaciligos la kodon. Fakte, ne tiom la uzado de esceptoj ŝarĝas la sistemon, sed prefere la ĵetado de eraroj mem per la ĵeto nova Escepto-konstruaĵo.

Ĵeti esceptojn estas pli malrapida ol iu klaso, kiu kolektos la eraron en la bezonata formato. Se vi prilaboras formularon aŭ iujn datumojn kaj vi klare scias, kio devus esti la eraro, kial ne procesi ĝin?

Vi ne devus skribi ĵeton novan Exception() konstrukcion se ĉi tiu situacio ne estas escepta. Pritrakti kaj ĵeti escepton estas tre multekosta!!!

AlMalsupra, AlMalsupraInvariant, AlSupre, AlSupreInvariant

Dum mia 5-jara sperto laboranta sur la platformo .NET, mi trovis multajn projektojn, kiuj uzis kordkongruon. Mi ankaŭ vidis la sekvan bildon: estis unu Enterprise-solvo kun multaj projektoj, ĉiu el kiuj elfaris kordkomparojn malsame. Sed kion oni devas uzi kaj kiel unuigi ĝin? En la libro CLR per C# de Richter, mi legis informojn, ke la metodo ToUpperInvariant() estas pli rapida ol ToLowerInvariant().

Eltiraĵo el la libro:

Rendimento en .NET Kerno

Kompreneble, mi ne kredis ĝin kaj decidis fari kelkajn provojn tiam sur la .NET Framework kaj la rezulto ŝokis min - pli ol 15% pliiĝo de rendimento. Poste, alveninte al la laboro la sekvan matenon, mi montris ĉi tiujn mezurojn al miaj superuloj kaj donis al ili aliron al la fontkodo. Post ĉi tio, 2 el 14 projektoj estis ŝanĝitaj por akomodi la novajn mezuradojn, kaj konsiderante ke ĉi tiuj du projektoj ekzistis por prilabori grandegajn Excel-tablojn, la rezulto estis pli ol signifa por la produkto.

Mi ankaŭ prezentas al vi mezurojn por malsamaj versioj de .NET Core, por ke ĉiu el vi povu elekti la plej optimuman solvon. Kaj mi nur volas aldoni, ke en la kompanio, kie mi laboras, ni uzas ToUpper() por kompari ŝnurojn.

kodo

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

Rendimento en .NET Kerno

Rendimento en .NET Kerno

En .NET Core 3.0, la pliiĝo por ĉiu el ĉi tiuj metodoj estas ~x2 kaj ekvilibrigas la efektivigojn inter si.

Rendimento en .NET Kerno

Rendimento en .NET Kerno

Tier-Kompilo

En mia lasta artikolo mi priskribis ĉi tiun funkcion mallonge, mi ŝatus korekti kaj kompletigi miajn vortojn. Multnivela kompilo plirapidigas la ektempon de via solvo, sed vi oferas, ke partoj de via kodo estos kompilitaj en pli optimumigitan version en la fono, kiu povas enkonduki malgrandan superkoston. Kun la apero de NET Core 3.0, la konstrutempo por projektoj kun nivela kompilo ebligita malpliiĝis kaj cimoj asociitaj kun ĉi tiu teknologio estis korektitaj. Antaŭe, ĉi tiu teknologio kaŭzis erarojn en la unuaj petoj en ASP.NET Core kaj frostiĝas dum la unua konstruo en plurnivela kompilreĝimo. Ĝi estas nuntempe ebligita defaŭlte en .NET Core 3.0, sed vi povas malŝalti ĝin se vi deziras. Se vi estas en la pozicio de teamgvidanto, altranga, meza, aŭ vi estas la estro de fako, tiam vi devas kompreni, ke rapida disvolvo de projekto pliigas la valoron de la teamo kaj ĉi tiu teknologio permesos al vi ŝpari tempon por ambaŭ programistoj. kaj la tempo de la projekto mem.

.NET-nivelo supren

Ĝisdatigu vian .NET Framework / .NET Core version. Ofte, ĉiu nova versio provizas pliajn rendimentajn gajnojn kaj aldonas novajn funkciojn.

Sed kio ĝuste estas la avantaĝoj? Ni rigardu kelkajn el ili:

  • .NET Core 3.0 enkondukis R2R-bildojn, kiuj reduktos la ektempon de .NET Core-aplikoj.
  • Kun versio 2.2 aperis Tier Compilation, danke al kiu programistoj pasigos malpli da tempo lanĉante projekton.
  • Subteno por novaj .NET-Normoj.
  • Subteno por nova versio de la programlingvo.
  • Optimumigo, kun ĉiu nova versio la optimumigo de la bazaj bibliotekoj Collection/Struct/Stream/String/Regex kaj multe pli plibonigas. Se vi migras de la .NET Framework al .NET Core, vi ricevos grandan rendimentan akcelon el la skatolo. Kiel ekzemplo, mi aldonas ligilon al iuj optimumigoj aldonitaj al .NET Core 3.0: https://devblogs.microsoft.com/dotnet/performance-improvements-in-net-core-3-0/

Rendimento en .NET Kerno

konkludo

Kiam vi verkas kodon, indas atenti malsamajn aspektojn de via projekto kaj uzi la funkciojn de via programlingvo kaj platformo por atingi la plej bonan rezulton. Mi ĝojus, se vi dividas viajn sciojn rilate al optimumigo en .NET.

Ligo al github

fonto: www.habr.com

Aldoni komenton