Qui n’a jamais eu à consolider deux listes en PowerShell ? Et comme moi, vous en avez marre de prendre un café en attendant la fin du script … Et bien, je vais vous donner une petite astuce pour accélérer drastiquement vos temps d’exécution 🙂

HashTable … kesako ?

Alors non, ce n’est pas une technique ancestrale pour découper des tables. Je ne suis pas un bucheron, mais si je suis parfois un peu bourrin 😉
Une HashTable est une liste indexée. Et c’est là que tout va se jouer : sur l’index.

Prenons, un exemple : j’ai une liste de 5000 utilisateurs avec les attributs : GUID et displayName
Si vous recherchez le displayName pour un GUID donné, vous devez faire une requête comme celle-ci

$monuser = $userList |where-object {$_.GUID -eq 'aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee'}

Le problème est que pour récupérer la ligne qui nous intéresse, on parcours toutes les lignes une par une et on teste l’attribut qui nous intéresse. Pas très performant, vous en conviendrez.

Maintenant, imaginez que nous avons construit une HashTable des utilisateurs avec le GUID comme index. Notre commande PowerShell devient

$monuser = maHashTable['aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee']

C’est plus simple à lire et les temps d’exécution font rêver …

La démonstration

Dans mon exemple, je vais partir d’une liste d’évènements. Chaque évènement identifie un utilisateur par son GUID. De l’autre côté, j’ai la liste des utilisateurs avec le displayName associé au GUID.
Je vais créer les listes :

function create-users {
    param(
        $count = 5000
    )

    foreach ($i in 0..$count) {
        [PSCustomObject]@{
            guid = new-guid
            displayName = "Kevin-{0}" -f $i
        }
    }
}

function create-events {
    param(
        $count=10000,
        $guidList
    )

    foreach ($i in 0..$count) {
        [PSCustomObject]@{
            id = new-guid
            userid = $guidList |get-random
            eventid = get-random
        }
    }
}

# Je génère mes listes
$users = create-users
$events = create-events -guidList $users.guid

Maintenant, je peux parcourir mes évènements et associé le displayName à chaque évènement et mesurer le temps d’exécution

Tout d’abord par la méthode habituelle

$starttime1 = get-date
foreach ($e in $events) {
    $e|add-member -notepropertyname "displayName1" -notepropertyvalue ($users|? {$_.guid -eq $e.userid}).displayName -force
}
$duration1 = ((get-date) - $starttime1).totalseconds

Ensuite en passant par une hashtable


# Je stocke mes utilisateurs dans une hashtable indexée par GUID
$usersHashtable = @{}
$users |% { $usersHashtable[$_.guid] = $_ }

# j'ajoute l'attribut displayname à mes évènements par la hashtable
$starttime2 = get-date
foreach ($e in $events) {
    $e|add-member -notepropertyname "displayName2" -notepropertyvalue $usersHashtable[$e.userid].displayName -force
}
$duration2 = ((get-date) - $starttime2).totalseconds

Et maintenant je compare les résultats pour une liste de 5000 utilisateurs et 10000 évènements :

  • Méthode standard : 771,7362843 secondes
  • Méthode HashTable : 6,6859321 secondes

Je pense qu’il n’y a pas photo 🙂
Voilà, vous allez pourvoir réduire les temps d’exécution de vos scripts. Pour ceux d’entre-vous qui font tourner des Runbook PowerShell sur Azure dans un Automation Account, vous allez faire des économies !!!

By Kevin

Leave a Reply

Your email address will not be published. Required fields are marked *