Performanse u .NET Core

Pozdrav svima! Ovaj članak predstavlja zbirku najboljih praksi koje ja i moji kolege primenjujemo već dugo vremena na raznim projektima.
Informacije o mašini na kojoj su izvršena merenja:BenchmarkDotNet=v0.11.5, OS=Windows 10.0.18362
Intel Core i5-8250U CPU 1.60GHz (Kaby Lake R), 1 CPU, 8 logičkih i 4 fizičke jezgre
.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 protiv ToArray i Ciklus
Ove informacije sam planirao pripremiti s izlaskom .NET Core 3.0, ali su me pretekli. Ne želim krasti tuđu slavu i kopirati informacije, pa ću samo navesti .
Od sebe želim predstaviti svoja merenja i rezultate, dodao sam obrnute cikluse za ljubitelje „C++ stila“ pisanja ciklusa.
Kod:
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;
}
}
Brzina rada u .NET Core 2.2 i 3.0 je gotovo identična. Evo šta sam uspeo dobiti u .NET Core 3.0:


Možemo zaključiti da je ciklična obrada kolekcije tipa Array brža zbog svojih unutarnjih optimizacija i jasnog određivanja veličine kolekcije. Također, važno je imati na umu da kolekcija tipa List ima svoje prednosti i trebate koristiti odgovarajuću kolekciju ovisno o potrebnim izračunima. Čak i ako pišete logiku za rad s petljama, ne zaboravite da je to obična petlja i također može biti podložna optimizacijama petlji. Na Habu je već davno objavljen članak: . Još uvijek je aktualan i preporučuje se za čitanje.
Baci
Prije godinu dana radio sam u tvrtki na legacy projektu, u kojem je bilo uobičajeno obrađivati validaciju polja putem konstrukcije try-catch-throw. Već tada sam shvatio da je to nezdrava poslovna logika rada projekta, stoga sam se, koliko je bilo moguće, trudio ne koristiti takvu konstrukciju. No, hajde da razjasnimo, što je loše u pristupu obrade grešaka na ovakav način. Napisao sam mali kod kako bih usporedio dva pristupa i snimio "benčeve" za svaku varijantu.
Kod:
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;
}Rezultati u .NET Core 3.0 i Core 2.2 imaju sličan rezultat (.NET Core 3.0):


Try catch otežava razumijevanje koda i povećava vrijeme izvođenja vaše programa. No, ako vam je potrebna ova konstrukcija, nemojte ubacivati one redove koda od kojih se ne očekuje obrada grešaka — to će olakšati razumijevanje koda. U stvarnosti, sustav ne opterećuje toliko obrada iznimaka, koliko izbacivanje samih grešaka preko konstrukcije throw new Exception.
Izbacivanje iznimaka radi sporije od nekog razreda koji će prikupiti grešku u potrebnom formatu. Ako obrađujete obrazac ili neke podatke i jasno znate koja bi greška trebala biti, zašto ih ne obraditi?
Nema smisla pisati konstrukciju throw new Exception() ako ta situacija nije iznimna. Obrada i isključivanje iznimki jako je skupa!!!
ToLower, ToLowerInvariant, ToUpper, ToUpperInvariant
U svom petogodišnjem iskustvu rada na .NET platformi naletio sam na mnogo projekata koji su koristili usporedbu nizova. Također sam vidio sljedeću sliku: bilo je jedno Enterprise rješenje s mnogo projekata, od kojih je svaki uspoređivao nizove na različite načine. Ali što koristiti i kako to unificirati? U knjizi CLR via C# Ričtera pročitao sam informaciju da metoda ToUpperInvariant() radi brže od ToLowerInvariant().
Izvadak iz knjige:

Naravno, nisam vjerovao i odlučio sam provesti neka testiranja tada još na .NET Framework i rezultat me šokirao — više od 15% poboljšanja performansi. Sljedećeg jutra nakon dolaska na posao pokazao sam te podatke svom šefu i dao mu pristup izvorima. Nakon toga su 2 od 14 projekata promijenjena prema novim mjerenjima, a s obzirom na to da su ti projekti postojali kako bi obradili ogromne excel tablice, rezultat je bio više nego značajan za proizvod.
Također vam predstavljam mjerenja za različite verzije .NET Core, kako bi svaki od vas mogao odabrati najoptimalnije rješenje. A samo želim dodati da u tvrtki u kojoj radim, koristimo ToUpper() za usporedbu nizova.
Kod:
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();
}


U .NET Core 3.0 poboljšanje za svaku od ovih metoda je ~x2 i balansira implementacije između njih.


Tier Compilation
U svom prethodnom članku ukratko sam opisao ovu funkcionalnost, a sada bih želeo da ispravim i dopunim svoje reči. Višeslojna kompilacija ubrzava vreme pokretanja vašeg rešenja, ali žrtvujete to što će delovi vašeg koda biti kompilovani u optimizovanu verziju u pozadini, što može dovesti do malih troškova. Sa dolaskom .NET Core 3.0 smanjeno je vreme izgradnje projekata sa uključenom višeslojnom kompilacijom, a ispravljene su greške vezane za ovu tehnologiju. Ranije je ova tehnologija dovodila do grešaka kod prvih zahteva u ASP.NET Core, kao i do blokada pri prvoj izgradnji u režimu višeslojne kompilacije. Trenutno je u .NET Core 3.0 uključena po defaultu, ali je možete isključiti po želji. Ako ste na poziciji team-lead, senior, middle ili rukovodilac odeljenja, treba da razumete da brza izrada projekta povećava vrednost tima i ova tehnologija će vam omogućiti da štedite vreme kako programerima, tako i samo vreme izrade projekta.
.NET nadogradnja
Povećajte verziju vašeg .NET Frameworka / .NET Corea. Često svaka nova verzija donosi dodatno poboljšanje u performansama i dodaje nove funkcije.
Ali koje tačno prednosti? Hajde da razmotrimo neke od njih:
- U .NET Core 3.0 su se pojavili R2R imidži koji će omogućiti smanjenje vremena pokretanja .NET Core aplikacija.
- Sa verzijom 2.2 uvedena je Višeslojna kompilacija, zahvaljujući kojoj programeri troše manje vremena na pokretanje projekta.
- Podrška za nove standarde .NET Standard.
- Podrška za novu verziju programskog jezika.
- Optimizacija, sa svakom novom verzijom poboljšava se optimizacija osnovnih biblioteka Collection/Struct/Stream/String/Regex i još mnogo toga. Ako prelazite sa .NET Frameworka na .NET Core, dobićete velika poboljšanja u performansama odmah. Kao primer, prilažem link na deo optimizacija koje su dodate u .NET Core 3.0:

Zaključak
Prilikom pisanja koda treba obratiti pažnju na različite aspekte vašeg projekta i koristiti funkcije vašeg programskog jezika i platforme za postizanje najboljeg rezultata. Biću zahvalan ako podelite svoja znanja vezana za optimizaciju u .NET.
Izvor: habr.com
