Ydeevne i .NET Core

Ydeevne i .NET Core

Ydeevne i .NET Core

Hej alle! Denne artikel er en samling af Best Practices, som mine kolleger og jeg har brugt i lang tid, når vi har arbejdet på forskellige projekter.

Oplysninger om den maskine, som beregningerne blev udført på:BenchmarkDotNet=v0.11.5, OS=Windows 10.0.18362
Intel Core i5-8250U CPU 1.60GHz (Kaby Lake R), 1 CPU, 8 logiske og 4 fysiske kerner
.NET Core SDK=3.0.100
[Vært]: .NET Core 2.2.7 (CoreCLR 4.6.28008.02, CoreFX 4.6.28008.03), 64bit RyuJIT
Core: .NET Core 2.2.7 (CoreCLR 4.6.28008.02, CoreFX 4.6.28008.03), 64bit RyuJIT
[Vært]: .NET Core 3.0.0 (CoreCLR 4.700.19.46205, CoreFX 4.700.19.46214), 64bit RyuJIT
Core: .NET Core 3.0.0 (CoreCLR 4.700.19.46205, CoreFX 4.700.19.46214), 64bit RyuJIT

Job=Core Runtime=Core

ToList vs ToArray og Cycles


Jeg planlagde at forberede disse oplysninger med udgivelsen af ​​.NET Core 3.0, men de slog mig til det, jeg vil ikke stjæle andres ære og kopiere andres oplysninger, så jeg vil bare påpege link til en god artikel, hvor sammenligningen er beskrevet i detaljer.

På mine egne vegne vil jeg bare præsentere mine målinger og resultater for dig; Jeg tilføjede omvendte loops til dem for elskere af "C++-stilen" med at skrive loops.

Kode:

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

Ydeevnehastigheder i .NET Core 2.2 og 3.0 er næsten identiske. Her er hvad jeg kunne få i .NET Core 3.0:

Ydeevne i .NET Core

Ydeevne i .NET Core

Vi kan konkludere, at iterativ behandling af en Array-samling er hurtigere på grund af dens interne optimeringer og eksplicitte samlingsstørrelsesallokering. Det er også værd at huske på, at en List-samling har sine egne fordele, og du bør bruge den rigtige samling afhængigt af de nødvendige beregninger. Selvom du skriver logik til at arbejde med loops, så glem ikke, at dette er en almindelig loop, og den er også underlagt mulig loop-optimering. En artikel blev publiceret på habr for ganske lang tid siden: https://habr.com/ru/post/124910/. Den er stadig relevant og anbefalet læsning.

Kast

For et år siden arbejdede jeg i en virksomhed på et legacy projekt, i det projekt var det normalt at behandle feltvalidering gennem en try-catch-throw konstruktion. Jeg forstod allerede dengang, at dette var usund forretningslogik for projektet, så når det var muligt, forsøgte jeg ikke at bruge sådan et design. Men lad os finde ud af, hvorfor tilgangen til håndtering af fejl med sådan en konstruktion er dårlig. Jeg skrev en lille kode for at sammenligne de to tilgange og lavede benchmarks for hver mulighed.

Kode:

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

Resultaterne i .NET Core 3.0 og Core 2.2 har et lignende resultat (.NET Core 3.0):

Ydeevne i .NET Core

Ydeevne i .NET Core

Try catch gør koden sværere at forstå og øger eksekveringstiden for dit program. Men hvis du har brug for denne konstruktion, bør du ikke indsætte de kodelinjer, som ikke forventes at håndtere fejl - det vil gøre koden lettere at forstå. Faktisk er det ikke så meget håndteringen af ​​undtagelser, der indlæser systemet, men snarere selve fejludsendelsen gennem throw new Exception-konstruktionen.

At smide undtagelser er langsommere end nogle klasse, der vil indsamle fejlen i det krævede format. Hvis du behandler en formular eller nogle data, og du tydeligt ved, hvad fejlen skal være, hvorfor så ikke behandle den?

Du bør ikke skrive en throw new Exception()-konstruktion, hvis denne situation ikke er usædvanlig. Det er meget dyrt at håndtere og smide en undtagelse!!!

ToLower, ToLowerInvariant, ToUpper, ToUpperInvariant

I løbet af mine 5 års erfaring med at arbejde på .NET platformen, er jeg stødt på mange projekter, der brugte string matching. Jeg så også følgende billede: Der var én Enterprise-løsning med mange projekter, som hver udførte strengsammenligninger forskelligt. Men hvad skal bruges, og hvordan forener man det? I bogen CLR via C# af Richter læste jeg information om, at ToUpperInvariant()-metoden er hurtigere end ToLowerInvariant().

Uddrag fra bogen:

Ydeevne i .NET Core

Selvfølgelig troede jeg ikke på det og besluttede at køre nogle test dengang på .NET Framework, og resultatet chokerede mig - mere end 15% ydelsesforøgelse. Så, da jeg ankom på arbejde næste morgen, viste jeg disse målinger til mine overordnede og gav dem adgang til kildekoden. Herefter blev 2 ud af 14 projekter ændret for at imødekomme de nye målinger, og i betragtning af at disse to projekter eksisterede for at behandle enorme Excel-tabeller, var resultatet mere end væsentligt for produktet.

Jeg præsenterer også for jer målinger for forskellige versioner af .NET Core, så I hver især kan træffe et valg hen imod den mest optimale løsning. Og jeg vil lige tilføje, at i den virksomhed, hvor jeg arbejder, bruger vi ToUpper() til at sammenligne strenge.

Kode:

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

Ydeevne i .NET Core

Ydeevne i .NET Core

I .NET Core 3.0 er stigningen for hver af disse metoder ~x2 og balancerer implementeringerne indbyrdes.

Ydeevne i .NET Core

Ydeevne i .NET Core

Tier Compilation

I min sidste artikel beskrev jeg denne funktionalitet kort, jeg vil gerne rette og supplere mine ord. Multi-level kompilering fremskynder opstartstiden for din løsning, men du ofrer, at dele af din kode bliver kompileret til en mere optimeret version i baggrunden, hvilket kan introducere en lille overhead. Med fremkomsten af ​​NET Core 3.0 er byggetiden for projekter med tier-kompilering aktiveret faldet, og fejl forbundet med denne teknologi er blevet rettet. Tidligere førte denne teknologi til fejl i de første anmodninger i ASP.NET Core og fryser under den første build i multi-level compilation mode. Det er i øjeblikket aktiveret som standard i .NET Core 3.0, men du kan deaktivere det, hvis du ønsker det. Hvis du er i stillingen som team-lead, senior, middle, eller du er leder af en afdeling, så skal du forstå, at hurtig projektudvikling øger værdien af ​​teamet, og denne teknologi vil give dig mulighed for at spare tid for begge udviklere og tidspunktet for selve projektet.

.NET niveau op

Opgrader din .NET Framework/.NET Core-version. Ofte giver hver ny version yderligere ydeevnegevinster og tilføjer nye funktioner.

Men hvad er fordelene helt præcist? Lad os se på nogle af dem:

  • .NET Core 3.0 introducerede R2R-billeder, der vil reducere opstartstiden for .NET Core-applikationer.
  • Med version 2.2 dukkede Tier Compilation op, takket være hvilken programmører vil bruge mindre tid på at starte et projekt.
  • Understøttelse af nye .NET-standarder.
  • Understøttelse af en ny version af programmeringssproget.
  • Optimering, med hver ny version forbedres optimeringen af ​​basisbibliotekerne Collection/Struct/Stream/String/Regex og meget mere. Hvis du migrerer fra .NET Framework til .NET Core, får du et stort ydelsesboost ud af boksen. Som et eksempel vedhæfter jeg et link til nogle af de optimeringer, der blev tilføjet til .NET Core 3.0: https://devblogs.microsoft.com/dotnet/performance-improvements-in-net-core-3-0/

Ydeevne i .NET Core

Konklusion

Når du skriver kode, er det værd at være opmærksom på forskellige aspekter af dit projekt og bruge funktionerne i dit programmeringssprog og platform for at opnå det bedste resultat. Jeg ville blive glad, hvis du deler din viden relateret til optimering i .NET.

Link til github

Kilde: www.habr.com

Tilføj en kommentar