Как эффективно заполнить массив в Powershell

Я хочу как можно быстрее заполнить динамический массив тем же целочисленным значением с помощью Powershell.
Команда Measure - показывает, что для ее заполнения требуется 7 секунд в моей системе.
Мой текущий код (обрезанный) выглядит так:

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

(полный код можно увидеть на gist.github.com или на суперпользователя)

считают, что $length может измениться. Но для лучшего понимания я выбрал фиксированный длина.

Q: Как ускорить этот код Powershell?

4 ответов


вы можете повторить массивы, так же, как вы можете сделать со строками:

$myArray = ,2 * $length

это означает " возьмите массив с одним элементом 2 и повторить его $length раз, давая новый массив.".

обратите внимание, что вы не можете использовать это для создания многомерных массивов, потому что следующее:

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

просто создаст 1000 ссылок на внутренний массив, что сделает их бесполезными для манипуляций. В этом случае можно использовать гибридную стратегию. У меня есть используется

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

в прошлом, но ниже измерения производительности предположить, что

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

было бы гораздо быстрее.


некоторые измерения производительности:

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

принято путем выполнения каждого варианта 50 раз через Measure-Command, каждый с одинаковым значением для $length, и усреднения результатов.

позиция 3 и 4 - это немного сюрприз, на самом деле. По-видимому, это намного лучше foreach над рядом вместо с помощью обычной for петли.


код для генерации выше диаграммы:

$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

избежать добавления к массиву в цикле. Он копирует существующий массив в новый массив с каждой итерацией. Вместо этого:

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

С помощью PowerShell 3.0 можно использовать (требуется .NET Framework 3.5 или выше):

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

Использование PowerShell 2.0

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

не ясно, что вы пытаетесь. Я пытался посмотреть твой код. Но,$myArray +=2 означает, что вы просто добавляете 2 в качестве элемента. Например, вот вывод из моего тестового кода:

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

2
2 2
2 2 2

почему вам нужно добавить 2 в качестве элемента массива так много раз?

Если все, что вы хотите, это просто заполнить то же значение, попробуйте следующее:

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