jeudi 20 novembre 2014

Optimisation - Powershell

Bonjour,

Aujourd'hui je vous propose un petit article sur les différentes façons d'optimiser vos scripts powershell. Après quelques recherches sur le net je suis en effet tombé sur des solutions intéressantes.


Commençons tout de suite avec notre premier type d'optimisation : le temps d'exécution.

Performance 

Les boucles : 

En ce qui concerne les différentes possibilités de la boucle For, on retrouve classiquement le même fonctionnement que sur d'autres langages. 

Ainsi tester la condition dans la boucle comme ceci : 

$range = 1..100000            
            
For($i=0; $i -lt $range.Count; $i++) {            
    $i            
}
est 4 fois plus lent qu'en effectuant le check de l'objet range avant la boucle. (source)


For vs Foreach :

La boucle For est 40 à 50 fois plus rapide que la boucle foreach. Vous trouverez la comparaison ici.

Il est donc évident que lorsque vous avez la possibilité d'effectuer un count sur votre objet avant de faire votre boucle, si votre problématique est sur la vitesse d'exécution et non forcément la mémoire utilisée, alors l'utilisation de la boucle for est indispensable.


Utiliser -Filter : 

Il arrive fréquemment de lister quelque chose, et de filtrer notre résultat sur un nom par exemple. Et bien il existe une option sur certaines cmdlets appelé Filter. 

Celle-ci est 4 à 8 fois plus performante que le classique "$something | where{$_.name -eq "votre nom"}" puisque -Filter effectue la sélection lors de l'exécution de votre requête et ne sélectionne que les objets répondant à votre critère, là où votre Where-Object va tous les sélectionner pour ensuite rechercher ceux qui correspondent à votre chaîne. (source


Check de conditions :

Encore une fois quelque chose que l'on retrouve dans de nombreux langages. Un petit bout de code parle mieux que de longues explications : 

Code 1 : 
if(1 -eq $true)
{
   Do-Something
}

Code 2 :
if(1)
{
   Do-Something
}

Je pense que vous voyez où je veux en venir ;). (source)


Consommation mémoire

Comme la vitesse d'exécution ne fait pas tout, et qu'il est parfois nécessaire de contrôler un minimum la taille mémoire de ce que l'on récupère (notamment lors de collecte massive de données). Voici quelques petites informations que j'ai trouvé à droite et à gauche sur la toile.

Tout d'abord afin de déterminer la consommation mémoire de votre instance PowerShell : 
[System.gc]::gettotalmemory("forcefullcollection") /1MB


Pipe vs Var


Lorsque l'on récupère un ensemble d'objets grâce à une cmdlet et que l'on souhaite les retraiter par la suite, on a plusieurs possibilités.

On peut soit :
  • Stocker nos objets dans une variable et les parcourir avec un ForEach (ce qui garde tous les objets en mémoire pour la session PowerShell)
  • Passer directement nos objets un par un à notre foreach avec un pipe

Code 1 : 

$something = Get-Something
foreach($test in $something)
{
   Do-Something
}

Code 2 :

Get-Something | foreach {Do-Something}

A noter que cela répond à une situation bien précise où vous souhaitez optimiser l'utilisation mémoire plus particulièrement. En effet le code 2 sera plus soft sur la mémoire mais sera également plus lent. (source)


Sélectionner les attributs


Les objets powershell sont gros, très gros et même parfois trop gros. Il est donc nécessaire de limiter ses récupérations à certains attributs si tous ne nous intéressent pas.

Là encore on peut retrouver une commande de type "Get-Something | select anotherthing". Cela fait le travail, mais on a la possibilité de le faire de façon plus élégante avec certaines options de cmdlet (si celles-ci sont présentes car elles ne sont pas implémentées pour toutes les cmdlets.

On retrouve ainsi : -DontUseDefaultIncludedProperties et -IncludedProperties

Tout comme pour l'utilisation de -Filter, il est préférable d'utiliser ces deux options si celles-ci sont implémentées sur les cmdlets que vous utilisez puisque vous ne récupérerez alors que les attributs nécessaires et demandés et ce dès le début de votre requête (ce qui soulage votre réseau si vous requêtez du VMware ou de l'AD par exemple et accélère celle-ci). 

Je pense que leur nom est suffisamment parlant, il suffit de spécifier les propriétés que vous souhaitez récupérer après -IncludedProperties et le tour est joué. (source)


Garbage Collector


On ne peut pas parler d'optimisation mémoire sans aborder le garbage collector utilisé en PowerShell. En effet si vous vous êtes déjà intéressé à l'empreinte mémoire de vos scripts, et que vous avez cherché des solutions pour un équivalent du "Free" du C par exemple, vous avez très certainement du vous retrouver avec des réponses du genre "$var = $null".

Il se trouve que le garbage collector se charge en effet de nettoyer tous ces objets inutiles, notamment ceux présents dans vos boucles que vous ne réutiliserez plus jamais par la suite. Néanmoins, celui-ci ne passe pas forcément au bon moment et il existe donc quelques astuces pour forcer son appel.

Ainsi, le garbage collector ne passera pas lorsque vous effectuez une boucle et il attendra que celle-ci se termine, ce qui peut poser problème si vous décidez de collecter un nombre important de données dans une bonne grosse boucle de la mort (là encore je pense plus particulièrement aux personnes tapant des AD, qui récupèrent facilement de gros volumes de données).

Pour représenter un peu tout ça, j'ai effectué rapidement un bonne boucle bien sale vous faisant exploser votre mémoire : 

$a = @{}
$i = 1

while(1)
{
    $a[$i] = $i    
    $i++

}

Et si on veut appeler notre petit garbage collector dans notre boucle :  [System.GC]::Collect()

Vous pourrez tout de suite constater la différence d'évolution de la mémoire utilisée. Attention néanmoins à cette utilisation puisque appeler le garbage collector lorsque celui-ci n'est pas prévu peut faire effondrer vos performances (à utiliser donc avec précaution). (source)

Je pense avoir fait le tour de tout ce que j'ai pu trouver jusqu'à présent. Si jamais vous avez d'autres informations n'hésitez pas et partagez.

Aucun commentaire:

Enregistrer un commentaire