Question

Le système sur lequel je travaille ici a été écrit avant .net 2.0 et ne bénéficiait pas des avantages des génériques. Il a finalement été mis à jour à la version 2.0, mais aucun code n'a été refactoré en raison de contraintes de temps. Il existe un certain nombre d'endroits où le code utilise des tableaux ArraysLists, etc., qui stockent des éléments sous forme d'objets.

Du point de vue des performances, quelle importance revêt le code d'utiliser des génériques? Je sais que, du point de vue de la performance, de la boxe et du déballage, etc., c'est inefficace, mais quel gain de performance y aura-t-il à la changer? Est-ce que les génériques peuvent être utilisés à l'avenir, ou si le changement de performances est suffisant pour qu'un effort de conscience soit nécessaire pour mettre à jour l'ancien code?

Était-ce utile?

La solution

Techniquement, la performance des génériques est, comme vous le dites, meilleure. Cependant, à moins que les performances ne soient extrêmement importantes ET que vous n'ayez déjà optimisé que dans d'autres domaines, vous obtiendrez BEAUCOUP de meilleures améliorations en passant votre temps ailleurs.

Je suggérerais:

  • utiliser les génériques à l'avenir.
  • si vous avez des tests unitaires solides, refacturez les génériques lorsque vous touchez le code
  • passez plus de temps à effectuer des refactorisations / mesures qui amélioreront considérablement les performances (appels à la base de données, modification des structures de données, etc.) plutôt que quelques millisecondes ici et là.

Bien sûr, il existe des raisons autres que la performance pour passer aux génériques:

  • moins sujet aux erreurs, car vous avez la vérification des types à la compilation
  • plus lisible, vous n'avez pas besoin de diffuser partout et il est évident que le type est stocké dans une collection
  • si vous utilisez des génériques à l'avenir, il est plus propre de les utiliser partout

Autres conseils

Voici les résultats d'une analyse simple d'une chaîne d'un fichier de 100 Ko, 100 000 fois. Il a fallu 612,293 secondes à la liste générique (de caractères) pour parcourir 100 000 fois le fichier. Il a fallu 2,880,415 secondes à ArrayList pour parcourir 100 000 fois le fichier. Cela signifie que dans ce scénario (votre kilométrage variant ), la liste générique (de caractères) est 4,7 fois plus rapide.

Voici le code que j'ai parcouru 100 000 fois:

Public Sub Run(ByVal strToProcess As String) Implements IPerfStub.Run
    Dim genList As New ArrayList

    For Each ch As Char In strToProcess.ToCharArray
        genList.Add(ch)
    Next

    Dim dummy As New System.Text.StringBuilder()
    For i As Integer = 0 To genList.Count - 1
        dummy.Append(genList(i))
    Next

End Sub

 Public Sub Run(ByVal strToProcess As String) Implements IPerfStub.Run
     Dim genList As New List(Of Char)

     For Each ch As Char In strToProcess.ToCharArray
         genList.Add(ch)
     Next

     Dim dummy As New System.Text.StringBuilder()
     For i As Integer = 0 To genList.Count - 1
         dummy.Append(genList(i))
     Next
 End Sub

Le seul moyen de savoir avec certitude consiste à profiler votre code à l'aide d'un outil tel que dotTrace.

http://www.jetbrains.com/profiler/

Il est possible que la boxe / unboxing soit trivial dans votre application particulière et ne mérite pas une refactorisation. A l'avenir, vous devriez toujours envisager d'utiliser des génériques en raison de la sécurité du type à la compilation.

Les génériques, qu’ils soient Java ou .NET, doivent être utilisés pour la sécurité de la conception et des types, pas pour la performance. La substitution automatique est différente des génériques (objet essentiellement implicite pour les conversions de primitives) et, comme vous l'avez mentionné, vous ne devez PAS les utiliser à la place d'une primitive s'il doit y avoir beaucoup d'opérations arithmétiques ou autres qui entraîneront une baisse de performance du résultat répété. création / destruction implicite d'objets.

Dans l’ensemble, je suggérerais d’utiliser l’avenir, et de ne mettre à jour le code existant que s’il doit être nettoyé à des fins de sécurité / conception, et non de performances.

Cela dépend, la meilleure réponse est de profiler votre code et de voir. J'aime AQTime, mais plusieurs packages existent pour cela.

En général, si une liste de tableaux est utilisée BEAUCOUP, il peut être intéressant de la remplacer par une version générique. En réalité, il est fort probable que vous ne puissiez même pas mesurer la différence de performance. La boxe et le déballage sont des étapes supplémentaires, mais les ordinateurs modernes sont si rapides que cela ne fait presque aucune différence. Comme ArrayList n’est en réalité qu’un tableau normal avec un bon wrapper, vous obtiendrez probablement beaucoup plus de performances avec une meilleure sélection de structure de données (ArrayList.Remove is O (n)!) Qu’avec la conversion en génériques.

Edit: Outlaw Programmer a un bon point: vous allez toujours boxer et déballer avec des génériques, cela se produit simplement de manière implicite. Tout le code autour de la vérification des exceptions et des valeurs NULL de la diffusion et "is / as". les mots clés aideraient un peu si.

Les gains les plus importants, vous les trouverez dans les phases de maintenance. Les génériques sont beaucoup plus faciles à gérer et à mettre à jour, sans avoir à traiter de problèmes de conversion et de diffusion. Si c'est le code que vous visitez en permanence, alors prenez l'effort. Si ce code n’a pas été touché depuis des années, cela ne me dérangerait pas vraiment.

Qu'est-ce que l'autoboxing / unboxing a à voir avec les génériques? Ceci est juste une question de sécurité de type. Avec une collection non générique, vous devez explicitement reconvertir le type réel d'un objet. Avec les génériques, vous pouvez ignorer cette étape. Je ne pense pas qu'il y ait une différence de performance d'une manière ou d'une autre.

Mon ancienne société a effectivement examiné ce problème. Notre approche était la suivante: s’il est facile de refactoriser, faites-le; sinon (c’est-à-dire qu’il touchera trop de classes), laissez-le plus tard. Cela dépend vraiment de savoir si vous avez le temps de le faire ou s'il y a des éléments plus importants à coder (c'est-à-dire des fonctionnalités que vous devriez implémenter pour les clients).

Encore une fois, si vous ne travaillez pas sur quelque chose pour un client, allez-y et passez du temps à refactoriser. Cela améliorera la lisibilité du code pour vous-même.

Cela dépend de la quantité de votre code. Si vous liez ou affichez des listes volumineuses dans l'interface utilisateur, vous obtiendrez probablement un grand gain de performances.

Si votre ArrayList est simplement saupoudré ici et là, alors ce ne serait probablement pas un gros problème de le nettoyer, mais cela n’aurait pas non plus un impact important sur les performances globales.

Si vous utilisez beaucoup de tableaux Array dans votre code et qu'il serait extrêmement fastidieux de les remplacer (quelque chose qui peut avoir une incidence sur vos calendriers), vous pouvez adopter une approche si vous le modifiez. .

Cependant, l’essentiel est que les génériques sont beaucoup plus faciles à lire et plus stables dans l’application en raison de leur frappe puissante. Vous constaterez des gains non seulement en termes de performances, mais également en termes de maintenance et de stabilité du code. Si vous pouvez le faire rapidement, je dirais le faire.

Si vous pouvez obtenir l'adhésion du responsable du produit, je vous conseillerais de le nettoyer. Vous aimez plus votre code par la suite.

Si les entités dans ArrayLists sont des types d'objet, vous gagnerez un peu en ne les attribuant pas au type correct. S'il s'agit de types Value (structures ou primitives telles que Int32), le processus de boxing / unboxing ajoute beaucoup de temps système et les collections génériques devraient être beaucoup plus rapides.

Voici un article MSDN sur le sujet

Les performances des génériques sont bien meilleures, en particulier si vous utilisez un type de valeur (int, bool, struct, etc.) pour lequel vous obtiendrez un gain de performances remarquable.

  1. L'utilisation de Arraylist avec des types de valeur provoque la boxing / unboxing qui, si l'opération est effectuée plusieurs centaines de fois, est considérablement plus lent que l'utilisation de la liste générique.

  2. lors du stockage de types de valeur en tant qu'objet, vous aurez jusqu'à quatre fois la mémoire temps par élément. Bien que cette quantité ne videra pas votre RAM, la mémoire cache plus petite peut contenir moins d'éléments. Cela signifie qu'en itérant de longues collections, de nombreuses copies de la mémoire principale vers la mémoire cache ralentiraient votre application.

J'ai écrit à propos de ici .

L'utilisation de génériques signifie également que votre code sera simple et facile à utiliser si vous souhaitez exploiter des éléments tels que linq dans les versions ultérieures de c #.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top