Dites mauvais mot pour LINQ

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, ifetc., 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 «» !


image


« - » . 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.

image
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 — .


image


N

image


Voilà, , . — N=1. 10% 82% .



 .



, , «» — Linq-, :


image
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 .


All Articles