Pendant toute ma courte vie en tant que programmeur c #, j'ai pensĂ© que LINQ ne concerne pas principalement les performances du code, mais les performances du programmeur, la vitesse Ă laquelle il Ă©crit le code et non la vitesse Ă laquelle le processeur exĂ©cute ce code. Et les problĂšmes de performances du code et du programmeur sont rĂ©solus aprĂšs avoir identifiĂ© les goulots d'Ă©tranglement. Par consĂ©quent, j'Ă©cris souvent var groupedByDate = lst.GroupBy(item => item.IssueTime.Date).Select(âŠ).ToList()
et ne le fais pas à cause de dommages ou d'intentions malveillantes, mais il est tout simplement plus facile de déboguer le code. Pour la liste, placez simplement le curseur de la souris sur le texte de la variable et vous pouvez immédiatement voir s'il y a quelque chose ou non.
Début
AprĂšs avoir lu l'article «Article infructueux sur l'accĂ©lĂ©ration de la rĂ©flexion » et calmĂ© les Ă©motions sur «quelqu'un a tort sur Internet», je me suis demandĂ© s'il Ă©tait possible de rendre le code «LINQ to Objects» proche des performances en «manuel». Pourquoi exactement LINQ to Objects? Dans mon travail, j'utilise souvent uniquement les fournisseurs LINQ to Objects et LINQ to Entities. La performance de LINQ to Entities n'est pas critique pour moi; il est essentiel Ă quel point la requĂȘte SQL gĂ©nĂ©rĂ©e sera optimale pour le serveur de base de donnĂ©es cible et Ă quelle vitesse le serveur l'exĂ©cutera.
Comme base, j'ai dĂ©cidĂ© d'utiliser le projet de l'auteur. Contrairement aux exemples tirĂ©s d'Internet, oĂč un code est principalement comparĂ© comme integerList.Where(item => item > 5).Sum()
un code «manuel» contenant foreach
, if
etc., l'exemple de l'article m'a paru intéressant et vital.
La premiĂšre chose que j'ai faite a Ă©tĂ© d'utiliser une fonction de comparaison de chaĂźne unique. Dans le code source, dans les mĂ©thodes qui remplissent la mĂȘme fonction, mais situĂ©es dans les coins opposĂ©s de l'anneau, diffĂ©rentes mĂ©thodes sont utilisĂ©es, dans un cas variable0.Equals(constant0, StringComparison.InvariantCultureIgnoreCase)
et dans un autre variable0.ToUpperInvariant() ==constant0.ToUpperInvariant()
. Il est particuliĂšrement gĂȘnant pour la constante que chaque appel de mĂ©thode soit converti en majuscules. J'ai choisi la troisiĂšme option en utilisant dans les deux cas variable0.ToUpperInvariant() ==constant0InUpperCaseInvariant
.
Ensuite, tout le code qui n'Ă©tait pas directement liĂ© Ă la comparaison des performances LINQ et du code manuel a Ă©tĂ© supprimĂ©. Le premier Ă ĂȘtre interceptĂ© Ă©tait le code qui crĂ©e et initialise l'objet classe Mock<HabraDbContext>
. Quel est l'intĂ©rĂȘt de crĂ©er une connexion Ă la base de donnĂ©es pour chaque test, une fois suffit? Il a Ă©tĂ© dĂ©placĂ© au-delĂ des tests de performances.
IStorage _db;
[GlobalSetup]
public void Setup()
{
_db = MockHelper.InstanceDb();
}
private IStorage DBInstance => _db;
⊠â , ! Linq «» !

« - » . LINQ . , â .
, , LINQ vs «» . . . .
, , , ([Params(1, 10, 100, 1000)]
). . . . StatisticColumnRelStdDev
.
â FastHydrationLinq
, ManualHydrationLinq
â . , , (Fast)(Manual)(Slow)HydrationLinq vs (Fast)(Manual)(Slow)Hydration, ManualHydrationLinq
. FastHydrationLinq
- . .
ToArray
, ToDictionary
IEnumerable<T>
. , FastContactHydrator2
. - Action<Contact, string>
c ConcurrentDictionary<string, Action<Contact, string>>
IEnumerable<KeyValuePair<string, Action<Contact, string>>>
. GetContact2
, Sum
, .
protected override Contact GetContact2(IEnumerable<PropertyToValueCorrelation> correlations)
{
var contact = new Contact();
int dummySum = _propertySettersArray.Join(correlations, propItem => propItem.Key, corrItem => corrItem.PropertyName, (prop, corr) => { prop.Value(contact, corr.Value); return 1; }).Sum();
return contact;
}
ParseWithLinq
public static IEnumerable<KeyValuePair<string, string>> ParseWithLinq2(string rawData, string keyValueDelimiter = ":", string pairDelimiter = ";")
=> rawData.Split(pairDelimiter)
.Select(x => x.Split(keyValueDelimiter, StringSplitOptions.RemoveEmptyEntries))
.Select(x => x.Length == 2 ? new KeyValuePair<string, string>(x[0].Trim(), x[1].Trim())
: new KeyValuePair<string, string>(_unrecognizedKey, x[0].Trim()));
.
FastContactHydrator2
, , , , -, ( ParseWithLinq
ParseWithoutLinq
). . , , ToArray
. 10 var result = new List<PropertyToValueCorrelation>(10)
. 10? 42 , 10 . Fair .
. . GetFakeData
. , .
, , « » (RelStdDev). N=1000
.
:
- `ManualHydration`, `SlowHydration`, `FastHydration`, `ManualHydrationLinq`, `SlowHydrationLinq`, `FastHydrationLinq` - , ;
ManualHydrationFair
, ManualHydrationFairLinq
, FastHydrationFairLinq
â , ;FastHydrationLinq2
â , , - , LINQ;N
, ;Ratio
FastHydrationLinq2
.

Linq-
? â FastHydrationLinq2
ManualHydrationLinq
Ratio
. 26,361.37 ÎŒs 26,422.17 ÎŒs N=1000
. / N
. ManualHydrationFairLinq
, 8% , . FastHydrationFairLinq
, 1% -.
Fair-. , ManualHydrationFairLinq
8% . FastHydrationFairLinq
FastHydrationLinq
12%. Linq .
, . . , MockHelper.InstanceDb()
Setup
, . â , GetFakeData
, . , â . MockHelper.InstanceDb()
.
N
, MakeGarbage
. False
, True
â .

VoilĂ , , . â N=1
. 10% 82% .
⊠.
, , «» â Linq-, :

N=1
MakeGarbage=True
«»
, , , LINQ .
«Gen X» «Allocated» N=1
MakeGarbage=True
o , FastHydrationLinq2
| | Gen 0 | Gen 1 | Gen 2 | Allocated |
|«» | 20.5078 | 0.4883 | - | 63.95 KB |
|Linq- | 20.7520 | - | - | 63.69 KB |
ManualHydrationFairLinq
.
. , , â .
, ? . , N
[Params(1, 100)]
MakeGargabe=True
. «» â Linq-, . â , â Linq-, Linq â â â Linq. â . .
, , â Linq- . , . paint.net, . paint.net â paint.net . paint.net â «» , â Linq- .
â N
1, 10, 100, 1000 MakeGarbage
[ParamsAllValues]
. paint.net, â «» . â Linq- . paint.net Visual Studio â «» . , 80- . , Linq- 2%.
AprÚs avoir écrit les tests et analysé les résultats, mon opinion sur LINQ n'a pas changé - en l'utilisant, ma productivité en écriture de code est plus élevée que sans elle; Les performances de LINQ to Objects sont satisfaisantes. L'utilisation retardée de l'exécution du code LINQ comme moyen d'améliorer les performances n'a pas beaucoup de sens.
Si dans un projet il y a des problÚmes de performances dus au ramasse-miettes - la blessure à la naissance .net, alors le choix de cette technologie n'était probablement pas la meilleure solution.
Mon code de test est disponible ici .