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:
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):
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!!!
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:
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();
}
.NET Core 3.0:ssa kunkin menetelmän lisäys on ~x2 ja se tasapainottaa toteutukset keskenään.
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/
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ä.