2013-07-26 48 views
6

Voglio riempire un array dinamico con lo stesso valore intero il più velocemente possibile usando Powershell.
Il comando Misura indica che ci vogliono 7 secondi sul mio sistema per riempirlo.
Il mio codice corrente (ottenuto) si presenta come:Come riempire un array in modo efficiente in Powershell

$myArray = @() 
$length = 16385 
for ($i=1;$i -le $length; $i++) {$myArray += 2} 

(codice completo può essere visto sul gist.github.com o superuser)

Si consideri che $length può cambiare. Ma per una migliore comprensione ho scelto una lunghezza fissa.

Q: Come velocizzare questo codice PowerShell?

risposta

14

È possibile ripetere gli array, basta come puoi fare con le stringhe:

$myArray = ,2 * $length 

Ciò significa »Prendi l'array con il singolo elemento 2 e ripetilo $length volte, ottenendo un nuovo array.«.

Si noti che non si può davvero utilizzare questo per creare array multidimensionali perché il seguente:

$some2darray = ,(,2 * 1000) * 1000 

sarà solo creare 1000 riferimenti alla matrice interna, che li rende inutili per la manipolazione. In tal caso puoi usare una strategia ibrida. Ho usato

$some2darray = 1..1000 | ForEach-Object { ,(,2 * 1000) } 

in passato, ma le misure di sotto delle prestazioni suggerire che

$some2darray = foreach ($i in 1..1000) { ,(,2 * 1000) } 

sarebbe un modo molto più veloce.


Alcune misurazioni delle prestazioni:

Command             Average Time (ms) 
-------             ----------------- 
$a = ,2 * $length             0,135902 # my own 
[int[]]$a = [System.Linq.Enumerable]::Repeat(2, $length)   7,15362 # JPBlanc 
$a = foreach ($i in 1..$length) { 2 }        14,54417 
[int[]]$a = -split "2 " * $length        24,867394 
$a = for ($i = 0; $i -lt $length; $i++) { 2 }     45,771122 # Ansgar 
$a = 1..$length | %{ 2 }           431,70304 # JPBlanc 
$a = @(); for ($i = 0; $i -lt $length; $i++) { $a += 2 }  10425,79214 # original code 

Tratto eseguendo ogni variante 50 volte attraverso Measure-Command, ciascuna con lo stesso valore per $length, e la media dei risultati.

Le posizioni 3 e 4 sono in realtà una sorpresa. Apparentemente è molto meglio a foreach su un intervallo invece di utilizzare un normale ciclo for.


codice per generare sopra tabella:

$length = 16384 

$tests = '$a = ,2 * $length', 
     '[int[]]$a = [System.Linq.Enumerable]::Repeat(2, $length)', 
     '$a = for ($i = 0; $i -lt $length; $i++) { 2 }', 
     '$a = foreach ($i in 1..$length) { 2 }', 
     '$a = 1..$length | %{ 2 }', 
     '$a = @(); for ($i = 0; $i -lt $length; $i++) { $a += 2 }', 
     '[int[]]$a = -split "2 " * $length' 

$tests | ForEach-Object { 
    $cmd = $_ 
    $timings = 1..50 | ForEach-Object { 
     Remove-Variable i,a -ErrorAction Ignore 
     [GC]::Collect() 
     Measure-Command { Invoke-Expression $cmd } 
    } 
    [pscustomobject]@{ 
     Command = $cmd 
     'Average Time (ms)' = ($timings | Measure-Object -Average TotalMilliseconds).Average 
    } 
} | Sort-Object Ave* | Format-Table -AutoSize -Wrap 
+0

+1 e accettato. '$ myArray =, 2 * 16385' viene eseguito in 0,01s – nixda

+0

+1 è una bella corsa! – JPBlanc

+0

+1 Conciso, chiaro, costruttivo, completo e riproducibile! (beh, 4 su 5 c ...) –

1

Non è chiaro cosa si stia provando. Ho provato a guardare il tuo codice. Ma, $myArray +=2 significa che stai solo aggiungendo 2 come elemento. Per esempio, ecco è l'uscita dal mio codice di prova:

$myArray = @() 
$length = 4 
for ($i=1;$i -le $length; $i++) { 
    Write-Host $myArray 
    $myArray += 2 
} 

2 
2 2 
2 2 2 

Perché avete bisogno di aggiungere 2 come elemento dell'array così tante volte?

Se invece si è solo riempire lo stesso valore, provate questo:

$myArray = 1..$length | % { 2 } 
+1

Lui è solo riempiendo la matrice con qualche valore? il valore è '2' – JPBlanc

+0

La domanda dice che vuole riempire l'array con lo stesso valore intero. Il suo problema è che accodare l'array con '+ =' è terribilmente lento. –

+0

Hmm! Io ho capito quello. Ma perché? Perché anche trovare un modo migliore per fare qualcosa che non è necessario. Ad ogni modo, può usare anche l'operatore di intervallo. – ravikanth

5

Evitare aggiungendo ad un array in un ciclo. Copia l'array esistente in un nuovo array con ogni iterazione. Fate questo invece:

$MyArray = for ($i=1; $i -le $length; $i++) { 2 } 
+0

+1 Per la spiegazione. – JPBlanc

+0

+1 '$ MyArray = for ($ i = 1; $ i -le 16385; $ i ++) {2}' viene eseguito in 0,05 secondi. molto più veloce dei miei 7s :) – nixda

3

Utilizzando PowerShell 3.0 è possibile utilizzare (bisogno di .NET Framework 3.5 o superiore):

[int[]]$MyArray = ([System.Linq.Enumerable]::Repeat(2, 65000)) 

Utilizzando PowerShell 2,0

$AnArray = 1..65000 | % {2} 
+0

+1 '[int []] $ myArray = ([System.Linq.Enumerable] :: Repeat (2, 16385))' viene eseguito in 0,03s – nixda

Problemi correlati