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
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
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
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 ' * 10000B- le codeur:
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
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'
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 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
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 3la convertion, à priori, n'impacte pas beaucoup sur le temps d'execution.
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
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 Cce type de codage souffre de quelques lacunes:
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
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'..
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
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.
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,9303le temps d'execution sera plus rapide.
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
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,5421car 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 PS > measure-command {foreach($a in [int[]](1..10000)){$a}} TotalMilliseconds : 19,5541
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) :
pour faire un simple test et voir maintenant la difference:
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--)
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
Partager