Teljesítmény a .NET Core-ban

Teljesítmény a .NET Core-ban

Teljesítmény a .NET Core-ban

Sziasztok! Ez a cikk a legjobb gyakorlatok gyűjteménye, amelyeket kollégáimmal régóta használunk különböző projekteken való munkánk során.

Információk arról a gépről, amelyen a számításokat elvégezték:BenchmarkDotNet=v0.11.5, OS=Windows 10.0.18362
Intel Core i5-8250U CPU 1.60 GHz (Kaby Lake R), 1 CPU, 8 logikai és 4 fizikai mag
.NET Core SDK=3.0.100
[Host]: .NET Core 2.2.7 (CoreCLR 4.6.28008.02, CoreFX 4.6.28008.03), 64 bites RyuJIT
Mag: .NET Core 2.2.7 (CoreCLR 4.6.28008.02, CoreFX 4.6.28008.03), 64 bites RyuJIT
[Host]: .NET Core 3.0.0 (CoreCLR 4.700.19.46205, CoreFX 4.700.19.46214), 64 bites RyuJIT
Mag: .NET Core 3.0.0 (CoreCLR 4.700.19.46205, CoreFX 4.700.19.46214), 64 bites RyuJIT

Job=Core Runtime=Core

ToList vs ToArray és Cycles


Terveztem, hogy a .NET Core 3.0 kiadásával elkészítem ezt az információt, de megvertek, nem akarom ellopni valaki más dicsőségét és lemásolni mások információit, ezért csak kiemelem. link egy jó cikkre, ahol részletesen le van írva az összehasonlítás.

A magam nevében csak a méréseimet és eredményeimet szeretném bemutatni, ezekhez fordított hurkokat adtam a „C++ stílus” íráshurkok kedvelőinek.

Kód:

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

A .NET Core 2.2 és 3.0 teljesítménye szinte azonos. A következőket sikerült megszereznem a .NET Core 3.0-ban:

Teljesítmény a .NET Core-ban

Teljesítmény a .NET Core-ban

Arra a következtetésre juthatunk, hogy egy tömbgyűjtemény iteratív feldolgozása gyorsabb a belső optimalizálás és az explicit gyűjteményméret-allokáció miatt. Azt is érdemes megjegyezni, hogy a List gyűjteménynek megvannak a maga előnyei, és a megfelelő gyűjteményt kell használnia a szükséges számításoktól függően. Még ha logikát ír is a hurkokkal való munkavégzéshez, ne felejtse el, hogy ez egy közönséges hurok, és az esetleges hurokoptimalizálásnak is alá esik. Elég régen megjelent egy cikk a habron: https://habr.com/ru/post/124910/. Továbbra is aktuális és ajánlott olvasmány.

Dobás

Egy évvel ezelőtt dolgoztam egy cégnél egy örökölt projekten, abban a projektben normális volt, hogy a terepellenőrzést egy try-catch-throw konstrukcióval dolgozták fel. Már akkor megértettem, hogy ez egészségtelen üzleti logika a projekthez, ezért lehetőség szerint igyekeztem nem ilyen kialakítást alkalmazni. De nézzük meg, miért rossz a hibakezelés megközelítése egy ilyen konstrukcióval. Írtam egy kis kódot a két megközelítés összehasonlítására, és referenciaértékeket készítettem az egyes lehetőségekhez.

Kód:

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

A .NET Core 3.0 és Core 2.2 eredményei hasonlóak (.NET Core 3.0):

Teljesítmény a .NET Core-ban

Teljesítmény a .NET Core-ban

A try catch nehezebbé teszi a kód megértését, és megnöveli a program végrehajtási idejét. De ha szüksége van erre a konstrukcióra, ne illessze be azokat a kódsorokat, amelyek várhatóan nem kezelik a hibákat - ez megkönnyíti a kód megértését. Valójában nem is annyira a kivételek kezelése tölti be a rendszert, hanem inkább maguk a hibák dobása a throw new Exception konstrukción keresztül.

A kivételek dobása lassabb, mint néhány olyan osztály, amely a hibát a kívánt formátumban gyűjti össze. Ha egy űrlapot vagy néhány adatot dolgoz fel, és egyértelműen tudja, mi lehet a hiba, miért ne dolgozná fel?

Ne írj új Exception() konstrukciót, ha ez a helyzet nem kivételes. A kivétel kezelése és dobása nagyon drága!!!

ToLower, ToLowerInvariant, ToUpper, ToUpperInvariant

A .NET platformon végzett 5 éves tapasztalatom során számos olyan projekttel találkoztam, amelyek karakterlánc-illesztést használtak. A következő képet is láttam: volt egy Enterprise-megoldás, sok projekttel, amelyek mindegyike eltérően hajtotta végre a karakterlánc-összehasonlításokat. De mit érdemes használni és hogyan lehet egységesíteni? A Richter CLR via C# című könyvében azt olvastam, hogy a ToUpperInvariant() metódus gyorsabb, mint a ToLowerInvariant().

Részlet a könyvből:

Teljesítmény a .NET Core-ban

Természetesen nem hittem el, és úgy döntöttem, hogy futtatok néhány tesztet a .NET-keretrendszeren, és az eredmény sokkolt – több mint 15%-os teljesítménynövekedés. Másnap reggel munkába érve megmutattam ezeket a méréseket a feletteseimnek, és hozzáférést adtam a forráskódhoz. Ezt követően 2 projektből 14-ben módosult az új méréseknek megfelelően, és tekintve, hogy ez a két projekt hatalmas Excel táblák feldolgozására létezett, az eredmény több mint jelentős volt a termék szempontjából.

Méréseket is bemutatok a .NET Core különböző verzióihoz, hogy mindenki döntse el a legoptimálisabb megoldást. És csak azt szeretném hozzátenni, hogy abban a cégben, ahol dolgozom, a ToUpper()-t használjuk a karakterláncok összehasonlítására.

Kód:

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

Teljesítmény a .NET Core-ban

Teljesítmény a .NET Core-ban

A .NET Core 3.0-ban ezeknek a metódusoknak a növekedése ~x2, és egyensúlyba hozza a megvalósításokat egymás között.

Teljesítmény a .NET Core-ban

Teljesítmény a .NET Core-ban

Tier összeállítás

Legutóbbi cikkemben röviden ismertettem ezt a funkciót, szeretném javítani és kiegészíteni szavaimat. A többszintű fordítás felgyorsítja a megoldás indítási idejét, de fel kell áldoznia, hogy a kód egyes részei a háttérben optimalizáltabb verzióba kerüljenek, ami kis többletköltséget jelenthet. A NET Core 3.0 megjelenésével lecsökkent a rétegösszeállítást engedélyező projektek felépítési ideje, és kijavították az ezzel a technológiával kapcsolatos hibákat. Korábban ez a technológia hibákhoz vezetett az ASP.NET Core első kéréseiben, és lefagyott az első összeállítás során többszintű fordítási módban. Jelenleg alapértelmezés szerint engedélyezve van a .NET Core 3.0-ban, de tetszés szerint letilthatja. Ha Ön csapatvezető, vezető, középső pozícióban van, vagy egy osztály vezetője, akkor meg kell értenie, hogy a gyors projektfejlesztés növeli a csapat értékét, és ez a technológia lehetővé teszi, hogy időt takarítson meg mindkét fejlesztő számára. és magának a projektnek az ideje.

.NET szinttel feljebb

Frissítse a .NET Framework / .NET Core verzióját. Gyakran minden új verzió további teljesítménynövekedést és új funkciókat ad hozzá.

De pontosan mik az előnyei? Nézzünk ezek közül néhányat:

  • A .NET Core 3.0 bevezette az R2R lemezképeket, amelyek csökkentik a .NET Core alkalmazások indítási idejét.
  • A 2.2-es verzióval megjelent a Tier Compilation, aminek köszönhetően a programozók kevesebb időt töltenek a projekt elindításával.
  • Új .NET szabványok támogatása.
  • A programozási nyelv új verziójának támogatása.
  • Optimalizálás, minden új verzióval javul a Gyűjtemény/Struktúra/Stream/String/Regex és még sok más alapkönyvtár optimalizálása. Ha a .NET-keretrendszerről a .NET Core-ra költözik, nagy teljesítménynövekedést kaphat. Példaként csatolok egy hivatkozást a .NET Core 3.0-hoz hozzáadott néhány optimalizáláshoz: https://devblogs.microsoft.com/dotnet/performance-improvements-in-net-core-3-0/

Teljesítmény a .NET Core-ban

Következtetés

A kód írásakor érdemes odafigyelni a projekt különböző aspektusaira, és a legjobb eredmény elérése érdekében felhasználni a programozási nyelv és a platform szolgáltatásait. Örülnék, ha megosztaná tudását a .NET-ben történő optimalizálással kapcsolatban.

Link a githubhoz

Forrás: will.com

Hozzászólás