Suorituskyky .NET Coressa

Suorituskyky .NET Coressa

Suorituskyky .NET Coressa

Hei kaikki! Tämä artikkeli on kokoelma parhaita käytäntöjä, joita kollegani ja minä olemme käyttäneet pitkään työskennellessämme eri projekteissa.

Tietoja koneesta, jolla laskelmat suoritettiin:BenchmarkDotNet=v0.11.5, OS=Windows 10.0.18362
Intel Core i5-8250U CPU 1.60 GHz (Kaby Lake R), 1 suoritin, 8 loogista ja 4 fyysistä ydintä
.NET Core SDK = 3.0.100
[Isäntä]: .NET Core 2.2.7 (CoreCLR 4.6.28008.02, CoreFX 4.6.28008.03), 64-bittinen RyuJIT
Ydin: .NET Core 2.2.7 (CoreCLR 4.6.28008.02, CoreFX 4.6.28008.03), 64-bittinen RyuJIT
[Isäntä]: .NET Core 3.0.0 (CoreCLR 4.700.19.46205, CoreFX 4.700.19.46214), 64-bittinen RyuJIT
Ydin: .NET Core 3.0.0 (CoreCLR 4.700.19.46205, CoreFX 4.700.19.46214), 64-bittinen RyuJIT

Työ=Ydin Runtime=Ydin

ToList vs ToArray ja Cycles


Ajattelin valmistella nämä tiedot .NET Core 3.0:n julkaisun myötä, mutta he lyövät minut siihen, en halua varastaa jonkun muun kunniaa ja kopioida muiden tietoja, joten huomautan vain linkki hyvään artikkeliin, jossa vertailu on kuvattu yksityiskohtaisesti.

Omasta puolestani haluan vain esitellä mittaukseni ja tulokseni, lisäsin niihin käänteiset silmukat kirjoitussilmukoiden ”C++-tyylistä” ystäville.

Koodi:

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

NET Core 2.2:n ja 3.0:n suorituskykynopeudet ovat lähes samat. Tässä on mitä sain .NET Core 3.0:ssa:

Suorituskyky .NET Coressa

Suorituskyky .NET Coressa

Voimme päätellä, että Array-kokoelman iteratiivinen käsittely on nopeampaa sen sisäisten optimointien ja eksplisiittisen kokoelman koon allokoinnin ansiosta. On myös hyvä muistaa, että List-kokoelmalla on omat etunsa ja sinun tulee käyttää oikeaa kokoelmaa tarvittavista laskelmista riippuen. Vaikka kirjoittaisit logiikkaa silmukoiden kanssa työskentelyyn, älä unohda, että tämä on tavallinen silmukka ja se on myös mahdollisen silmukan optimoinnin alainen. Artikkeli julkaistiin habrissa melko kauan sitten: https://habr.com/ru/post/124910/. Se on edelleen ajankohtainen ja suositeltavaa luettavaa.

Heittää

Vuosi sitten työskentelin eräässä yrityksessä perinnöllisen projektin parissa, siinä projektissa oli normaalia käsitellä kentän validointia try-catch-throw -konstruktion kautta. Ymmärsin jo silloin, että tämä oli epäterveellistä bisneslogiikkaa projektille, joten aina kun mahdollista yritin olla käyttämättä tällaista mallia. Mutta selvitetään, miksi lähestymistapa virheiden käsittelyyn tällaisessa rakenteessa on huono. Kirjoitin pienen koodin vertaillakseni kahta lähestymistapaa ja tein vertailuarvot kullekin vaihtoehdolle.

Koodi:

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

.NET Core 3.0:n ja Core 2.2:n tuloksilla on samanlainen tulos (.NET Core 3.0):

Suorituskyky .NET Coressa

Suorituskyky .NET Coressa

Kokeile catch tekee koodista vaikeampaa ymmärtää ja pidentää ohjelman suoritusaikaa. Mutta jos tarvitset tätä rakennetta, älä lisää niitä koodirivejä, joiden ei odoteta käsittelevän virheitä - tämä tekee koodista helpompi ymmärtää. Itse asiassa järjestelmää ei kuormita niinkään poikkeusten käsittely, vaan itse virheiden heittäminen uuden Exception-rakenteen kautta.

Poikkeusten heittäminen on hitaampaa kuin jokin luokka, joka kerää virheen vaaditussa muodossa. Jos käsittelet lomaketta tai tietoja ja tiedät selvästi, mikä virheen pitäisi olla, miksi et käsittele sitä?

Älä kirjoita uutta Exception()-konstruktia, jos tilanne ei ole poikkeuksellinen. Poikkeuksen käsittely ja heittäminen on erittäin kallista!!!

ToLower, ToLowerInvariant, ToUpper, ToUpperInvariant

Viiden vuoden kokemukseni .NET-alustalla työskentelystäni aikana olen törmännyt moniin projekteihin, joissa käytettiin merkkijonoa. Näin myös seuraavan kuvan: siellä oli yksi Enterprise-ratkaisu, jossa oli useita projekteja, joista jokainen suoritti merkkijonovertailut eri tavalla. Mutta mitä pitäisi käyttää ja miten se yhtenäistää? Richterin kirjasta CLR via C#, luin tietoa, että ToUpperInvariant()-menetelmä on nopeampi kuin ToLowerInvariant().

Ote kirjasta:

Suorituskyky .NET Coressa

En tietenkään uskonut sitä ja päätin suorittaa testejä sitten .NET Frameworkissa, ja tulos järkytti minua - yli 15 % suorituskyvyn kasvu. Sitten seuraavana aamuna töihin saavuttuani näytin nämä mittaukset esimiehilleni ja annoin heille pääsyn lähdekoodiin. Tämän jälkeen 2/14 projektia muutettiin vastaamaan uusia mittauksia, ja ottaen huomioon, että nämä kaksi projektia olivat olemassa valtavien Excel-taulukoiden käsittelyyn, tulos oli tuotteelle enemmän kuin merkittävä.

Esitän teille myös mittoja eri .NET Coren versioille, jotta jokainen voi valita optimaalisimman ratkaisun. Ja haluan vain lisätä, että yrityksessä, jossa työskentelen, käytämme ToUpper():tä merkkijonojen vertailuun.

Koodi:

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

Suorituskyky .NET Coressa

Suorituskyky .NET Coressa

.NET Core 3.0:ssa kunkin menetelmän lisäys on ~x2 ja se tasapainottaa toteutukset keskenään.

Suorituskyky .NET Coressa

Suorituskyky .NET Coressa

Tason kokoelma

Edellisessä artikkelissani kuvailin tätä toimintoa lyhyesti, haluaisin korjata ja täydentää sanojani. Monitasoinen käännös nopeuttaa ratkaisusi käynnistysaikaa, mutta uhraat sen, että osa koodistasi käännetään taustalla optimoidummaksi versioksi, mikä voi aiheuttaa pienen lisäkulun. NET Core 3.0:n myötä rakennusaika projekteille, joissa on käytössä tasojen käännös, on lyhentynyt ja tähän tekniikkaan liittyvät virheet on korjattu. Aiemmin tämä tekniikka johti virheisiin ASP.NET Coren ensimmäisissä pyynnöissä ja jumiutui ensimmäisen koontiversion aikana monitasoisessa käännöstilassa. Se on tällä hetkellä oletuksena käytössä .NET Core 3.0:ssa, mutta voit halutessasi poistaa sen käytöstä. Jos olet tiiminvetäjän, vanhemman, keskitason asemassa tai olet osastopäällikkö, sinun on ymmärrettävä, että nopea projektikehitys lisää tiimin arvoa ja tämän tekniikan avulla voit säästää aikaa molemmille kehittäjille. ja itse projektin aika.

.NET tason ylöspäin

Päivitä .NET Framework / .NET Core -versiosi. Usein jokainen uusi versio parantaa suorituskykyä ja lisää uusia ominaisuuksia.

Mutta mitä hyötyä niistä tarkalleen ottaen on? Katsotaanpa joitain niistä:

  • .NET Core 3.0 esitteli R2R-otokset, jotka lyhentävät .NET Core -sovellusten käynnistysaikaa.
  • Versiossa 2.2 ilmestyi Tier Compilation, jonka ansiosta ohjelmoijat viettävät vähemmän aikaa projektin käynnistämiseen.
  • Uusien .NET-standardien tuki.
  • Ohjelmointikielen uuden version tuki.
  • Optimointi, jokaisella uudella versiolla peruskirjastojen Kokoelma/Struct/Stream/String/Regex ja paljon muuta optimointi paranee. Jos olet siirtymässä .NET Frameworkista .NET Coreen, saat suuren suorituskyvyn parantua heti. Esimerkkinä liitän linkin joihinkin .NET Core 3.0:aan lisättyihin optimointiin: https://devblogs.microsoft.com/dotnet/performance-improvements-in-net-core-3-0/

Suorituskyky .NET Coressa

Johtopäätös

Koodia kirjoitettaessa kannattaa kiinnittää huomiota projektisi eri puoliin ja käyttää ohjelmointikielesi ja alustasi ominaisuuksia parhaan tuloksen saavuttamiseksi. Olisin iloinen, jos jakaisit tietosi optimoinnista .NET:ssä.

Linkki githubiin

Lähde: will.com

Lisää kommentti