IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Framework .NET Discussion :

LINQ VS "algo classique" = surprise !


Sujet :

Framework .NET

  1. #1
    Membre du Club
    Profil pro
    Inscrit en
    Août 2006
    Messages
    88
    Détails du profil
    Informations personnelles :
    Âge : 39
    Localisation : France, Savoie (Rhône Alpes)

    Informations forums :
    Inscription : Août 2006
    Messages : 88
    Points : 62
    Points
    62
    Par défaut LINQ VS "algo classique" = surprise !
    Bonjour à tous,

    Voila ce matin je me suis amusé à faire un petit test comparatif des perfs .NEt entre deux méthodes de développement...
    Et de manière assez stupéfiante je ne m'attendais pas à ça.

    Du coup je voulais vous faire partager histoire de reccueuillir vos avis et voir si quelqu'un a déjà fait d'autres tests et en a déduit une sorte de "best-practice"...

    Voici le code testé, tout simple, méthode 1 classique puis méthode 2 et 3 en LINQ.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
     
        Private Sub M1()
            Dim initList As New List(Of Integer)
            Dim resultList As New List(Of Integer)
            For i = 1 To 10000000
                initList.Add(i)
            Next
            For Each i In initList
                If i Mod 2 = 0 Then
                    resultList.Add(i)
                End If
            Next
        End Sub
     
        Private Sub M2()
            Dim initList As New List(Of Integer)
            Dim resultList As New List(Of Integer)
            For i = 1 To 10000000
                initList.Add(i)
            Next
            resultList.AddRange(initList.Where(Function(e) e Mod 2 = 0).ToList)
        End Sub
     
        Private Sub M3()
            Dim initList As New List(Of Integer)
            Dim resultList As New List(Of Integer)
            For i = 1 To 10000000
                initList.Add(i)
            Next
            For Each i In initList.Where(Function(e) e Mod 2 = 0)
                resultList.Add(i)
            Next
        End Sub
    Et voici ce qui ressort en résultat. Vous en pensez quoi ?

    Nom : imgTest.jpg
Affichages : 170
Taille : 24,2 Ko

  2. #2
    Expert confirmé
    Avatar de Pragmateek
    Homme Profil pro
    Formateur expert .Net/C#
    Inscrit en
    Mars 2006
    Messages
    2 635
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 37
    Localisation : France, Val de Marne (Île de France)

    Informations professionnelles :
    Activité : Formateur expert .Net/C#
    Secteur : Conseil

    Informations forums :
    Inscription : Mars 2006
    Messages : 2 635
    Points : 4 062
    Points
    4 062
    Par défaut
    Comment fais-tu les mesures, via des TimeSpans ou du Stopwatch ?

    Il faudrait des traitements plus longs pour que ce soit significatif.

    Sinon rien de choquant : passer par la tuyauterie LINQ a un coût.
    Formateur expert .Net/C#/WPF/EF Certifié MCP disponible sur Paris, province et pays limitrophes (enseignement en français uniquement).
    Mon blog : pragmateek.com

  3. #3
    Expert confirmé Avatar de DonQuiche
    Inscrit en
    Septembre 2010
    Messages
    2 741
    Détails du profil
    Informations forums :
    Inscription : Septembre 2010
    Messages : 2 741
    Points : 5 485
    Points
    5 485
    Par défaut
    Moi non plus je ne vois rien de surprenant.

    Quant à la meilleure pratique, c'est celle qui donne le code le plus clair et le plus lisible. Sauf dans les cas où les performances doivent réellement être améliorées (1% des cas).

  4. #4
    Membre du Club
    Profil pro
    Inscrit en
    Août 2006
    Messages
    88
    Détails du profil
    Informations personnelles :
    Âge : 39
    Localisation : France, Savoie (Rhône Alpes)

    Informations forums :
    Inscription : Août 2006
    Messages : 88
    Points : 62
    Points
    62
    Par défaut
    Bonjour à tous les deux et merci pour vos réponses

    Bon j'avoue que le titre était un peu racoleur.
    Effectivement avec un peu plus de recul et de réflexion, oui, rien de surprenant à ce que l'utilisation de LINQ ai un cout...

    Mais je ne m'attendais pas à un tel type d'écart, va savoir pourquoi je m'étais imaginé que LINQ apportait de l'optimisation en pagaille ou que sais je encore..
    Pour répondre à la première question j'utilise un stopwatch, pour répondre à la seconde remarque j'ai refait le même test avec 100 fois plus de calcul (en gros dans le premier test je générais une liste de 10 000 000 d'éléments integer dont je voulais conserver que les nombres pairs dans une seconde liste, ici je refais la même chose encadré dans une boucle for i = 0 to 99)

    Je trouve quand même que l'écart est très significatif.

    1,7 fois plus consommateur entre M1 classique
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
     
    For Each i In initList
        If i Mod 2 = 0 Then
            resultList.Add(i)
        End If
    Next
    et M2 Linq
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
    resultList.AddRange(initList.Where(Function(e) e Mod 2 = 0).ToList)
    1.5 fois plus consommateur avec M3 un peu plus proche de M1
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
     
    For Each i In initList.Where(Function(e) e Mod 2 = 0)
        resultList.Add(i)
    Next
    Nom : imgTest.jpg
Affichages : 133
Taille : 50,7 Ko

    Ceci étant pour répondre à la dernière remarque pour moi il n'y a pas photo en terme de lecture entre les deux méthodes d'écritures et je préfèrerais donc la M2 sur une seule ligne. Ce n'est pas non plus tous les 4 matins qu'on isole les nombres pairs dans une liste de 1 000 000 000 d'éléments après avoir généré ladite liste ...

  5. #5
    Expert confirmé Avatar de DonQuiche
    Inscrit en
    Septembre 2010
    Messages
    2 741
    Détails du profil
    Informations forums :
    Inscription : Septembre 2010
    Messages : 2 741
    Points : 5 485
    Points
    5 485
    Par défaut
    Citation Envoyé par zesamoth Voir le message
    va savoir pourquoi je m'étais imaginé que LINQ apportait de l'optimisation en pagaille ou que sais je encore..
    Après plus de dix années passées à travailler avec dotnet, j'en ai déduit la règle empirique suivante : pour chaque opportunité d'optimisation, le compilateur AOT et le compilateur JIT ignoreront tous deux cette opportunité et garantissent que rien ne sera fait pour optimiser les performances.

    Si tu veux des perfs, bousille ton code.

    Et encore... Tes deux autres exemples ont créé davantage d'objets et tu n'as pas pris en compte le temps pris par le ramasse-miettes. Chaque méthode linq va en effet nécessiter trois objets (un lambda, un énumérable et un énumérateur). Si tu dois énumérer un million d'éléments c'est négligeable mais ça ne l'est plus pour un petit nombre d'éléments.


    Bref. Il faut se faire à l'idée que ton code est obèse, ce qui en général ne pose pas de pb et produit tout de même des applis rapides. Mais le jour où tu auras un pb, la seule solution sera trop souvent de créer le code le plus dégueulasse au monde. Esxpérons que ryujit et dotnet native amélioreront un peu les choses mais après dix à quinze années de promesses déçues...

  6. #6
    Membre du Club
    Profil pro
    Inscrit en
    Août 2006
    Messages
    88
    Détails du profil
    Informations personnelles :
    Âge : 39
    Localisation : France, Savoie (Rhône Alpes)

    Informations forums :
    Inscription : Août 2006
    Messages : 88
    Points : 62
    Points
    62
    Par défaut
    Bonjour DonQuiche,

    Après plus de dix années passées à travailler avec dotnet, j'en ai déduit la règle empirique suivante : pour chaque opportunité d'optimisation, le compilateur AOT et le compilateur JIT ignoreront tous deux cette opportunité et garantissent que rien ne sera fait pour optimiser les performances.
    Au moins il y a des garanties, de quoi te plains tu !

    Merci pour la poursuite de la réponse.
    Ayant un peu moins d'expérience que toi mais en ayant un peu quand même je ne peux qu'approuver tes propos. Oui le code
    .NET est obèse....

    Après, comme tu le dis, la plupart du temps je ne me formalise pas dessus partant d'un principe simple : s'il la qualité du code permet d'optimiser les perfs de, mettons, 0.5 secondes au détriment d'une maintenance accrue d'une demie journée (4 heures pour arrondir) alors l'optimisation devient "rentable" au bout de 0.5 * 7200 * 4 = 28 800 occurrences. Donc, soit le contexte le justifie (réellement possible d'atteindre rapidement les 28 800 occurrences, expérience utilisateur dégradée et/ou frustrée, etc...) soit l'attente est clairement exprimée dans les spécifs.

    Donc la plupart du temps j'essaye de ne pas trop bousiller mon code sauf s'il le besoin est avéré.

    @ ++

    PS : Et on le voit, pour gagner 0.5 secondes sur un traitement c'est que le traitement est déjà balèze....

  7. #7
    Futur Membre du Club
    Homme Profil pro
    Développeur .NET
    Inscrit en
    Août 2014
    Messages
    5
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 37
    Localisation : Belgique

    Informations professionnelles :
    Activité : Développeur .NET
    Secteur : High Tech - Multimédia et Internet

    Informations forums :
    Inscription : Août 2014
    Messages : 5
    Points : 9
    Points
    9
    Par défaut
    Salut,

    Je me permet d'ajouter un version Linq presque aussi efficace que la version "faite main".
    J'ai repris ton code pour tester (la dernière version avec la boucle de 0 à 100) et voici mes résultats.

    Nom : Capture.PNG
Affichages : 119
Taille : 4,8 Ko

    Et voici le code de la méthode 4:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
     
    Private Sub M4()
        Dim resultList As List(Of Integer) = Enumerable.Range(1, 10000000).Where(Function(e) e Mod 2 = 0).ToList()
    End Sub
    Linq est basé sur le IEnumerable la méthode M4 n'effectue en qu'une seule boucle de 1 à 10000000 et seulement lors de l'appel à ToList().
    Les autres versions font respectivement:
    M1 -> 2 boucles / ajouts dans 2 listes
    M2 -> 3 boucles / ajouts dans 3 listes
    M3 -> 2 boucles / ajouts dans 2 listes

    Max.

Discussions similaires

  1. Quote et double quote
    Par aktos dans le forum Langage
    Réponses: 8
    Dernier message: 05/01/2007, 19h55

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo