Jõudlus .NET Core'is

Tere kõigile! See artikkel on parimate praktikate kogumik, mida mina ja mu kolleegid oleme pikka aega erinevate projektide raames rakendanud.
Teave masina kohta, millel arvutused tehti:BenchmarkDotNet=v0.11.5, OS=Windows 10.0.18362
Intel Core i5-8250U CPU 1.60GHz (Kaby Lake R), 1 CPU, 8 loogilist ja 4 füüsilist tuuma
.NET Core SDK=3.0.100
[Host]: .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
[Host]: .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 ja tsüklid
Seda teavet plaanisin valmistada koos .NET Core 3.0 väljalaskmisega, kuid mind ületati, ma ei taha varastada teiste kuulsust ja kopeerida nende teavet, seega lihtsalt mainin .
Oma poolt tahaksin esitada teile oma mõõtmised ja tulemused, olen lisanud neisse vastupidised tsüklid neile, kes armastavad ‘C++ stiilis’ tsüklite kirjutamist.
Kood:
public class Bench
{
private List _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 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 0; i--)
{
total += _array[i];
}
return total;
}
}
.NET Core 2.2 ja 3.0 töökiirus on peaaegu identsed. Siin on, mida ma sain .NET Core 3.0-s:


Võime järeldada, et Array tüüpi kollektsiooni tsükliline töötlemine on kiiremad tänu oma sisemistele optimeerimisele ja kollektsiooni suuruse selgele määratlemisele. Samuti tasub meeles pidada, et List tüüpi kollektsioonil on oma eelised ning peaksite kasutama sobivat kollektsiooni sõltuvalt vajalike arvutuste vajadusest. Isegi kui kirjutate silmade töös loogikat, ei tohi unustada, et see on tavaline tsükkel ja see on samuti allutatud tsüklite võimalikule optimeerimisele. Habl on juba pikka aega ilmunud artikkel: . See on endiselt aktuaalne ning soovitatav lugemiseks.
Throw
Aasta tagasi töötasin ettevõttes legacy-projektiga, kus oli tavapärane käsitleda väljaandestamine läbi try-catch-throw konstruktsiooni. Juba siis mõistsin, et see oli ebatervislik äri loogika projekti töös, seetõttu püüdsin võimalusel sellist konstruktsiooni vältida. Kuid vaatame, mis on halb lähtepunkt, et töötada sellise konstruktsiooniga vigu käsitledes. Kirjutasin väikese koodi, et võrrelda kahte lähenemist ja võtsin “benssi” igas variandi kohta.
Kood:
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;
}Tulemused .NET Core 3.0 ja Core 2.2 on sarnased (.NET Core 3.0):


Try catch raskendab koodi mõistmist ja suurendab teie programmi täitmisaja. Kuid kui vajate seda konstruktsiooni, ärge lisage neid koodirekke, mille puhul ei oodata viga — see lihtsustab koodi mõistmist. Tegelikult koormab süsteemi mitte niivõrd erandite töötlemine, kui viga loobumine läbi throw new Exception konstruktsiooni.
Erandite viskamine töötab aeglasemalt kui ükski klass, mis kogub vea sobivas formaadis. Kui töötlete vormi või andmeid ja teate täpselt, mis viga peaks olema, siis miks mitte neid töödelda?
Ei ole mõtet kirjutada konstruktsiooni throw new Exception(), kui see olukord ei ole erandlik. Erandi töötlemine ja viskamine on väga kallis!!!
ToLower, ToLowerInvariant, ToUpper, ToUpperInvariant
Oma 5 aastase töökogemuse jooksul .NET platvormil olen kohtunud mitmete projektidega, mis kasutasid stringide võrdlemist. Olen ka näinud järgmist pilti: oli üks ettevõtte lahendus, millel oli palju projekte, millest igaüks teostas stringide võrdlemist erinevalt. Aga mida tuleks kasutada ja kuidas seda ühtlustada? Raamatus CLR via C# Richters kirjutasin üles, et meetod ToUpperInvariant() töötab kiiremini kui ToLowerInvariant().
Lõige raamatust:

Muidugi ei uskunud ma sellele ja otsustasin teha mõned testid veel .NET Frameworkis ning tulemus šokeeris mind — üle 15% jõudluse tõus. Järgmisel hommikul näitasin neid andmeid oma ülemusele ja andsin neile juurdepääsu lähtekoodile. Pärast seda muudeti 2 14 projektist uute mõõtmiste jaoks, arvestades, et need kaks projekti töötasid tohutute Excel tabelite töötlemiseks, oli tulemus produkti jaoks rohkem kui oluline.
Esitan teile ka mõõtmised erinevate .NET Core versioonide jaoks, et igaüks teist võiks teha valiku kõige optimaalsema lahenduse suunas. Soovin vaid lisada, et ettevõttes, kus ma töötan, kasutame stringide võrdlemiseks ToUpper().
Kood:
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();
}


.NET Core 3.0-s on iga nende meetodite kasu ~x2 ja tasakaalustab rakendusi omavahel.


Tier Compilation
Oma eelmine artikkel käsitles seda funktsionaalsust lühidalt ning soovin oma sõnu parandada ja täiendada. Mitmeastmeline kompileerimine kiirendab teie lahenduse käivitusaega, kuid te ohverdite selle, et osa teie koodist kompileeritakse taustal rohkem optimeeritud versiooniks, mis võib kaasa tuua väikseid ülekandeid. NET Core 3.0 saabumisega on projektide koguaeg vähenenud, kui on lubatud tier kompileerimine ja fikseeriti sellega seotud vead. Varasemalt põhjustas see tehnoloogia ASP.NET Core esimestel päringutel vigu ja esmakordsel mitmeastmelise kompileerimise režiimis hangumist. Praeguseks on .NET Core 3.0 see vaikesätete sees, kuid võite selle soovimisel välja lülitada. Kui olete meeskonna juhi, vanema, keskastme spetsialisti või osakonna juhi ametis, peate mõistma, et projekti kiire arendamine suurendab meeskonna väärtust ja see tehnoloogia võimaldab teil kokku hoida nii arendajate aega kui ka projekti tööaega.
.NET tasand üles
Tõstke oma .NET Frameworki / .NET Core'i versioon üles. Sage sageli, et iga uus versioon pakub täiendavat jõudluse kasvu ja lisab uusi funktsioone.
Aga millised on konkreetsed eelised? Vaatame mõnda neist:
- .NET Core 3.0-s on saadaval R2R-pildid, mis aitavad vähendada .NET Core rakenduste käivitusaega.
- Alates versioonist 2.2 on olemas Tier Compilation, mille tõttu programmeerijad kulutavad projekti käivitamiseks vähem aega.
- Uute .NET Standardi standardite tugi.
- Uue programmeerimiskeele versiooni tugi.
- Optimeerimine, iga uue versiooniga paranevad baasraamatukogude Collection/Struct/Stream/String/Regex ja palju muud optimeerimine. Kui liigute .NET Frameworkilt .NET Core'i, saate kasutusele võtta suure jõudluse kasvu otse välja. Näiteks on siin link osa optimeerimiste kohta, mis viidi sisse .NET Core 3.0:

Kokkuvõte
Koodi kirjutamisel on oluline pöörata tähelepanu erinevatele aspekte teie projektist ning kasutada oma programmeerimiskeele ja platvormi funktsioone parima tulemuse saavutamiseks. Olen tänulik, kui jagate oma teadmisi, mis on seotud optimeerimisega .NET-is.
Allikas: habr.com
