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

Scripts/Batch Discussion :

Comment rendre vos codes plus rapides en execution


Sujet :

Scripts/Batch

  1. #1
    Membre chevronné
    Avatar de I'm_HERE
    Homme Profil pro
    Inscrit en
    Juillet 2008
    Messages
    1 013
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Tunisie

    Informations forums :
    Inscription : Juillet 2008
    Messages : 1 013
    Points : 1 991
    Points
    1 991
    Par défaut Comment rendre vos codes plus rapides en execution
    salut,

    En Scrpitng (et /ou programmation) gagner du temps d'execution et gagner en ressources utilisés reste un sujet attractive car il nous permet de comprendre comment nos codes sont évalués chose qui nous aidera à les optimiser...dans ce petit tuto, j'ai choisi quelques techniques qui vous permetteront de rendre vos codes plus rapide:

    1- opérateur sans Where/foreach

    dans beaucoup de cas on peux utiliser des opérateurs de comparaisons sans utiliser les cmdlets Where-object et foreach-object, ceci rendra notre code plus rapide à l'execution

    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
    PS > $items = @('item1','item2','item3','item4')
    PS > $items -eq 'item2'
    item2
    PS > $items | Where-Object { $_ -eq 'item2' }
    item2
    PS > $items -like '*2'
    item2
    PS > $items | Where-Object { $_ -like '*2' }
    item2
    PS >
    PS > $items -match 'item[0-2]'
    item1
    item2
    PS > $items | Where-Object { $_ -match 'item[0-2]' }
    item1
    item2
    on peux tester avec cet exmple pour voir la difference.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    PS > measure-command { 1..10000 | % {} {'#'} {} | Where {$_ -notlike '#'}}
    TotalMilliseconds : 3446,8914
    
    PS > measure-command { (1..10000 | % {} {'#'} {}) -notlike '#' }
    TotalMilliseconds : 1289,1554

    2- Regex et méthodes de chaines de caractères

    en Powershell on peux bénéficier de toute la puissance .NET et surtout son implementation des Regex qui reste l'un des moteur les plus puissant avec la librairie PCRE.
    Pour aider les admins à entrer dans ce monde 'de chasse au pattern' les concepteurs de PS ont créer des opérateurs (-split -replace -match), cmdlet (select-string) et des validateurs ([validatePattern()]) basé sur des recherche de pattern Regex, c'est super puissant, SI ON LE MAITRISE BIEN, et il peux nous aider à faire des formatage de données complexe en une seule ligne...c'est presque parfait, j'ai dit 'presque' parfait car malheureusement, nulle n'est parfait, est les Regexs malgré leurs puissance n'echappe pas à cette règle et peuvent impacter sur la performance de nos codes, cet impact se porte sur le moteur Regex en lui même et sur le codeur:

    A- le moteur Regex:

    en général, les méthodes des chaines de caractèrs sont plus performants en temps d'execution que les Regex car avec les Regex on a des étapes à parcourir avant même la recherche du pattern, ces etapes sont le formattage de l'expression et sa validité (pour voir ceci vous pouvez créer un faux pattern [regex]"\T") , la compilation ensuite l'execution.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    PS > $a = 'hell ' * 10000
    PS > $a += 'hello '
    PS > $a += 'hell ' * 10000
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    PS > measure-command { $a.Replace('hell ','') }
    
    TotalMilliseconds : 1,042
    
    PS > measure-command { $a -Replace 'hell ' }
    
    TotalMilliseconds : 25,5247
    B- le codeur:

    le codeur peux être, lui aussi, la cause de la mauvaise perforamnce de son pattern, et ceci en essayant de reinventer la roue, en utilisant les Regexp pour faire un traitement qu'on peux le faire facilement avec une méthode String deja existante.
    Aussi, si le codeur est débutant en Regex il peux créer des patterns consommant beaucoup de resources "memoire" et/ou "être victime" d'un catastrophic-backtracking

    3- les boucles:


    a- boucles foreach - foreach-object:


    les deux boucles 'foreach' pemettent d'itterer sur une collection d'objets,en faisant un petit test j'ai observé que L'instruction 'foreach' est plus rapide que la cmdlet 'foreach-object'

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    PS II> $items = 1..10000
    PS II> ( measure-command {foreach($item in $items){$item}} ).TotalMilliseconds
    27,5291
    PS II> ( measure-command {$items | foreach {$_}} ).TotalMilliseconds
    1377,0705
    pour comprendre la raison, l'instruction 'foreach' n'utilise pas le pipeline est sa variable '$item' est évalué avant son execution dans la boucle, au contraire de la cmdlet compilé 'foreach-object' qui utilise le pipeline...j'ai fait des tests supplementaires pour voir si la conversion de l'alias 'foreach' en son vrai nom 'foreach-object' peux causer un problème de performance..

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    PS II> $items = 1..3
    PS II> Trace-Command CommandDiscovery -ex { $items | foreach {$_} } -PSHost
    DÉBOGUER*: CommandDiscovery Information: 0 : Looking up command: foreach
    DÉBOGUER*: CommandDiscovery Information: 0 : Alias found: foreach  ForEach-Object
    DÉBOGUER*: CommandDiscovery Information: 0 : Cmdlet found: ForEach-Object
    1
    2
    3
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    PS II> Trace-Command CommandDiscovery -ex { $items | foreach-object {$_} } -PSHost
    DÉBOGUER*: CommandDiscovery Information: 0 : Looking up command: foreach-object
    DÉBOGUER*: CommandDiscovery Information: 0 : Cmdlet found: ForEach-Object
    1
    2
    3
    la convertion, à priori, n'impacte pas beaucoup sur le temps d'execution.

    b- boucles for while do-while

    ces boucle acceptent plus facilement les objets indexés:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    PS II> $items = [char[]]@(65..67)
    PS II> for($i=0; $i -lt $items.count; $i++) {$items[$i]}
    A
    B
    C
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    PS II> $a=0 ; while($a -lt $items.count) {$items[$a++]}
    A
    B
    C
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    PS II> $a=0 ; Do {$items[$a++]} While($a -lt $items.count)
    A
    B
    C
    ce type de codage souffre de quelques lacunes:

    A- propriété d'objet et cast

    tout d'abord on peux observé que la propriété 'count' de la liste $items est évalué dans le contexte de la boucle ceci va impliquer que l'evaluation va être répété jusqu'a ce que la boucle se termine et plus il y aurra de profondeur dans la propriété de l'objet plus l'evatuation sera plus longue, ceci peux être le cas avec des objets XML est du dot-notation '$doc.books.book.author.etc'..

    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
    PS II> $items = 1..3
    PS II> trace-command MemberResolution {for($i=0; $i -lt $items.count; $i++){}} -psh
    DÉBOGUER*: MemberResolution Information: 0 : Lookup
    DÉBOGUER*: MemberResolution Information: 0 :     "count" present in type table.
    DÉBOGUER*: MemberResolution Information: 0 : Lookup
    DÉBOGUER*: MemberResolution Information: 0 :     "Length" NOT present in type table.
    DÉBOGUER*: MemberResolution Information: 0 :     Adapted member: Length.
    DÉBOGUER*: MemberResolution Information: 0 : Lookup
    DÉBOGUER*: MemberResolution Information: 0 :     "count" present in type table.
    DÉBOGUER*: MemberResolution Information: 0 : Lookup
    DÉBOGUER*: MemberResolution Information: 0 :     "Length" NOT present in type table.
    DÉBOGUER*: MemberResolution Information: 0 :     Adapted member: Length.
    DÉBOGUER*: MemberResolution Information: 0 : Lookup
    DÉBOGUER*: MemberResolution Information: 0 :     "count" present in type table.
    DÉBOGUER*: MemberResolution Information: 0 : Lookup
    DÉBOGUER*: MemberResolution Information: 0 :     "Length" NOT present in type table.
    DÉBOGUER*: MemberResolution Information: 0 :     Adapted member: Length.
    DÉBOGUER*: MemberResolution Information: 0 : Lookup
    DÉBOGUER*: MemberResolution Information: 0 :     "count" present in type table.
    DÉBOGUER*: MemberResolution Information: 0 : Lookup
    DÉBOGUER*: MemberResolution Information: 0 :     "Length" NOT present in type table.
    DÉBOGUER*: MemberResolution Information: 0 :     Adapted member: Length.
    pour palier à ce problème on peux evaluer $items.count en dehors du contexte de la boucle en l'affectant à une variable, ceci va nous permettre d'évaluer la condition de sorti une seul fois au lieu de plusieurs fois:


    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    PS II> $items = 1..100000
    PS II> # $items.count est évaluer à chaque tour de boucle 
    PS II> Measure-Command {for($i=0; $i -lt $items.count; $i++){$items[$i]} }
    
    TotalMilliseconds : 3491,9303
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    PS II> $count = $items.count
    PS II> # la variable $count est évaluer une seule fois en dehors de la boucle
    PS II> Measure-Command {for($i=0; $i -lt $count; $i++){$items[$i]} }
    
    
    TotalMilliseconds : 569,9716
    le temps d'execution sera plus rapide.

    la même chose pour le cast, le cast d'une variable ou d'une collection dans une boucle peux impacter sur le temps d'execution de notre code:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    PS > measure-command {foreach($a in (1..10000)){$a}}
    
    TotalMilliseconds : 8,5421
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    PS > measure-command {foreach($a in [int[]](1..10000)){$a}}
    
    TotalMilliseconds : 19,5541
    car dans le contexte d'une boucle le cast s'effectue à chaque tour de boucle:


    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    PS > Trace-Command -Name TypeConversion -Expression {
    >>  foreach($a in (1..3)){$a}
    >> } -PSHost
    >>
    DÉBOGUER*: TypeConversion Information: 0 : Converting "64" to "System.Int32".
    DÉBOGUER*: TypeConversion Information: 0 :     Result type is assignable from value to convert's type
    DÉBOGUER*: TypeConversion Information: 0 : Converting "System.Object[]" to "System.Object[]".
    DÉBOGUER*: TypeConversion Information: 0 :     Result type is assignable from value to convert's type
    1
    2
    3
    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
    PS > Trace-Command -Name TypeConversion -Expression {
    >>  foreach($a in [int[]](1..3)){}
    >> } -PSHost
    >>
    DÉBOGUER*: TypeConversion Information: 0 : Converting "64" to "System.Int32".
    DÉBOGUER*: TypeConversion Information: 0 :     Result type is assignable from value to convert's type
    DÉBOGUER*: TypeConversion Information: 0 : Converting "System.Object[]" to "System.Object[]".
    DÉBOGUER*: TypeConversion Information: 0 :     Result type is assignable from value to convert's type
    DÉBOGUER*: TypeConversion Information: 0 : Conversion to System.Type
    DÉBOGUER*: TypeConversion Information: 0 : Converting "System.Object[]" to "System.Int32[]".
    DÉBOGUER*: TypeConversion Information: 0 :     Converting "1" to "System.Int32".
    DÉBOGUER*: TypeConversion Information: 0 :         Result type is assignable from value to convert's type
    DÉBOGUER*: TypeConversion Information: 0 :     Converting "2" to "System.Int32".
    DÉBOGUER*: TypeConversion Information: 0 :         Result type is assignable from value to convert's type
    DÉBOGUER*: TypeConversion Information: 0 :     Converting "3" to "System.Int32".
    DÉBOGUER*: TypeConversion Information: 0 :         Result type is assignable from value to convert's type

    B- Condition de sorti

    dans ces boucles on constate que la condition de sortie est évaluée deux fois: ($a < $count) AND ($a == TRUE)
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    PS II> $a=0 ; while($a -lt $count) {$items[$a++]}
    PS II> $a=0 ; Do {$items[$a++]} While($a -lt $count)

    ceci va impacter sur le temps d'execution de notre boucle. Le mieux est d'inverser la logique de nos boucles en rendant la condition de sorti évaluée une seule fois ($a-- == TRUE) :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    PS II> for($a=$count; $a--) {$items[$a]}
    PS II> $a=$count ; while($a--) {$items[$a]}
    PS II> $a=$count - 1 ; Do {$items[$a]} While($a--)
    pour faire un simple test et voir maintenant la difference:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    PS II> $items = 1..100000
    PS II> $a=0 ; measure-command {while($a -lt $items.count) {$items[$a++]}}
    TotalMilliseconds : 3567,7073
    PS II> $a=$items.count ; measure-command { while($a--) {$items[$a]} }
    TotalMilliseconds : 344,4985

  2. #2
    Membre expert
    Avatar de sachadee
    Homme Profil pro
    AMI DU BAT
    Inscrit en
    Janvier 2013
    Messages
    1 478
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Brésil

    Informations professionnelles :
    Activité : AMI DU BAT
    Secteur : Distribution

    Informations forums :
    Inscription : Janvier 2013
    Messages : 1 478
    Points : 3 768
    Points
    3 768
    Par défaut
    Un grand merci à toi I'm Here !

    Épatant comme toujours

  3. #3
    Rédacteur


    Profil pro
    Inscrit en
    Janvier 2003
    Messages
    7 171
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Janvier 2003
    Messages : 7 171
    Points : 15 060
    Points
    15 060
    Billets dans le blog
    1
    Par défaut
    Salut Walid,
    pour les regex, c'est la première exécution qui est coûteuse :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    $a = 'hell ' * 10000
    $a += 'hello '
    $a += 'hell ' * 10000
     
    measure-command { $a.Replace('hell ','')}
    measure-command { $a.Replace('hell ','')}
     
    measure-command { $a -Replace 'hell ' }
    measure-command { $a -Replace 'hell ' }
    On peut également éviter l'usage de l'opérateur '+=' sur les tableaux (System.Array), voir ce post.
    Le type System.Collections.ArrayList est préférable tout en évitant le piége de la valeur de retour sur la méthode Add :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    $T=New-Object System.Collections.ArrayList
     
    $T.Add(1)
    #renvoi 0
    #0 étant la valeur de l'index d'insertion
     
    [void]$T.Add(2)
    #ne renvoi rien
     
    $T
    #1
    #2

  4. #4
    Membre chevronné
    Avatar de I'm_HERE
    Homme Profil pro
    Inscrit en
    Juillet 2008
    Messages
    1 013
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Tunisie

    Informations forums :
    Inscription : Juillet 2008
    Messages : 1 013
    Points : 1 991
    Points
    1 991
    Par défaut
    Salut sachadee, laurent

    Citation Envoyé par Laurent Dardenne Voir le message
    Salut Walid,
    pour les regex, c'est la première exécution qui est coûteuse :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    $a = 'hell ' * 10000
    $a += 'hello '
    $a += 'hell ' * 10000
     
    measure-command { $a.Replace('hell ','')}
    measure-command { $a.Replace('hell ','')}
     
    measure-command { $a -Replace 'hell ' }
    measure-command { $a -Replace 'hell ' )
    Non laurent, la methode replace est plus rapide, essaye de faire une boucle de 1000 itération pour voir la différence

    [Void]$T.Add(2)
    $T
    #1
    #2
    [/code]
    au sujet de [void] j'ai fais un petit test et j'ai trouvé que la technique la plus rapide était $null=$Add (2)

Discussions similaires

  1. Comment rendre ce code plus propre ?
    Par n0-sheep dans le forum C++
    Réponses: 2
    Dernier message: 15/12/2013, 17h31
  2. Comment rendre un code MATLAB plus rapide?
    Par ERICKO dans le forum MATLAB
    Réponses: 9
    Dernier message: 04/07/2012, 17h47
  3. Comment rendre ce code plus "générique" ?
    Par Yann39 dans le forum jQuery
    Réponses: 14
    Dernier message: 10/11/2010, 10h42
  4. Comment rendre Shutdown immediate plus rapide ds Oracle9i
    Par mtaleb dans le forum Administration
    Réponses: 3
    Dernier message: 06/05/2009, 10h30
  5. Un moyen de rendre mon code plus rapide?
    Par Beluga_71 dans le forum Macros et VBA Excel
    Réponses: 5
    Dernier message: 14/05/2008, 10h36

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