Обработка больших массивов в PowerShell

Мне трудно понять, как наиболее эффективно обрабатывать большие наборы данных/массивы в PowerShell. У меня есть массивы с несколькими миллионами элементов, которые мне нужно обработать и сгруппировать. Этот список всегда разный по размеру, что означает, что он может быть 3,5 миллиона элементов или 10 миллионов элементов.

Пример: 3,5 миллиона элементов, которые они группируют по четвёркам, например:

Пункты 0,1,2,3 Сгруппировать вместе 4,5,6,7 Сгруппировать вместе и так далее.

Я попытался обработать массив с помощью одного потока, прокручивая список и назначая pscustomobject, который работает, для его завершения требуется всего 45-50+ минут.

Я также попытался разбить массив на более мелкие массивы, но это приводит к тому, что процесс выполняется еще дольше.

$i=0
$d_array = @()
$item_array # Large dataset


While ($i -lt $item_array.length){

    $o = "Test"
    $oo = "Test"
    $n = $item_array[$i];$i++
    $id = $item_array[$i];$i++
    $ir = $item_array[$i];$i++
    $cs = $item_array[$i];$i++

    $items = [PSCustomObject]@{
        'field1' = $o
        'field2' = $oo
        'field3' = $n
        'field4' = $id
        'field5' = $ir
        'field6'= $cs
    }
    $d_array += $items

}

Я полагаю, что если бы я применил планировщик заданий, который позволил бы мне запускать несколько заданий, это значительно сократило бы время процесса, но я хотел, чтобы другие взяли на себя быстрый и эффективный способ решения этой проблемы.


person BDubs    schedule 01.06.2019    source источник
comment
Первое, что вы должны попробовать, это не использовать добавление массива.   -  person user4003407    schedule 01.06.2019


Ответы (4)


Если вы работаете с большими данными, использование C# также эффективно.

Add-Type -TypeDefinition @"
using System.Collections.Generic;

public static class Test
{
    public static List<object> Convert(object[] src)
    {
        var result = new List<object>();
        for(var i = 0; i <= src.Length - 4; i+=4)
        {
            result.Add( new {
                field1 = "Test",
                field2 = "Test",
                field3 = src[i + 0],
                field4 = src[i + 1],
                field5 = src[i + 2],
                field6 = src[i + 3]
            });
        }
        return result;
    }
}
"@

$item_array = 1..10000000
$result = [Test]::Convert($item_array)
person rokumaru    schedule 01.06.2019
comment
Я собирался предложить именно то изменение, которое вы только что сделали сами, безусловно, самую быструю версию (+1). - person ; 01.06.2019
comment
Все здесь потрясающие, спасибо за ваш ответ. - person BDubs; 02.06.2019
comment
29 мс с моим примером. Ницца. - person js2010; 02.06.2019

Хотя версия rokumaru непревзойденна, здесь я попытаюсь использовать мои локальные измерения из js2010

Тот же $item_array = 1..100000 применяется ко всем версиям

> .\SO_56406847.ps1
measuring...BDups
measuring...LotPings
measuring...Theo
measuring...js2010
measuring...rokumaru
BDups    = 75,9949897 TotalSeconds
LotPings = 2,3663763 TotalSeconds
Theo     = 2,4469917 TotalSeconds
js2010   = 2,9198114 TotalSeconds
rokumaru = 0,0109287 TotalSeconds

## Q:\Test\2019\06\01\SO_56406847.ps1
$i=0
$item_array = 1..100000  # Large dataset

'measuring...LotPings'
$LotPings = measure-command {
    $d_array = for($i=0;$i -lt $item_array.length;$i+=4){
        [PSCustomObject]@{
            'field1' = "Test"
            'field2' = "Test"
            'field3' = $item_array[$i]
            'field4' = $item_array[$i+1]
            'field5' = $item_array[$i+2]
            'field6' = $item_array[$i+3]
        }
    }
} # measure-command
person Community    schedule 01.06.2019
comment
Это круто, но запятая в общем количестве секунд должна быть десятичной точкой, верно? - person js2010; 02.06.2019

Как это? в 32,5 раза быстрее. Создание массивов с += убивает щенков. Он копирует весь массив каждый раз.

$i=0
$item_array = 1..100000 # Large dataset

'measuring...'

# original 1 min 5 sec                                                                 
# mine 2 sec              
# other answer, 2 or 3 sec
# c# version 0.029 sec, 2241x faster!

measure-command {

$d_array = 
While ($i -lt $item_array.length){
    $o = "Test"
    $oo = "Test"
    $n = $item_array[$i];$i++                                                      
    $id = $item_array[$i];$i++                                                     
    $ir = $item_array[$i];$i++                                                     
    $cs = $item_array[$i];$i++      
    # $items =                                               
    [PSCustomObject]@{
        'field1' = $o
        'field2' = $oo
        'field3' = $n
        'field4' = $id
        'field5' = $ir
        'field6'= $cs
    }
    # $d_array += $items
}

}
person js2010    schedule 01.06.2019

Вы можете несколько оптимизировать это, используя ArrayList. или, возможно, даже лучше, используя строго типизированный List, но просмотр миллионов элементов в массиве все равно займет время..

Что касается вашего кода: нет необходимости сначала фиксировать значения элементов массива в переменной, а затем использовать их для добавления в PSCustomObject.

$item_array = 'a','b','c','d','e','f','g','h' # Large dataset
$result = New-Object System.Collections.Generic.List[PSCustomObject]
# or use an ArrayList: $result = New-Object System.Collections.ArrayList

$i = 0
While ($i -lt $item_array.Count) {
    [void]$result.Add(
        [PSCustomObject]@{
            'field1' = "Test" # $o
            'field2' = "Test" # $oo
            'field3' = $item_array[$i++]  #$n
            'field4' = $item_array[$i++]  #$id
            'field5' = $item_array[$i++]  #$ir
            'field6' = $item_array[$i++]  #$cs
        }
    )
}

# save to a CSV file maybe ?
$result | Export-Csv 'D:\blah.csv' -NoTypeInformation

Если вам нужно, чтобы результат снова стал «нормальным» массивом, используйте $result.ToArray()

person Theo    schedule 01.06.2019
comment
За исключением случая обеспечения совместимости с существующим кодом, я не думаю, что есть причина даже рассматривать возможность использования ArrayList. Документация ArrayList, которую вы связали с состояниями как таковыми: мы не рекомендуем использовать класс ArrayList для новой разработки. Вместо этого мы рекомендуем вам использовать универсальный класс List<T>. Возможно, разница между ними не так очевидна при использовании PowerShell, как это было бы, скажем, с компилируемым языком, таким как C#, но я все же думаю, что было бы хорошо не представлять устаревший класс в качестве жизнеспособной альтернативы. - person Lance U. Matthews; 01.06.2019
comment
@BACON AFAIK Arraylist еще не классифицирован obsolete. Рекомендуется больше не использовать его, так как есть лучшая альтернатива с List. Именно поэтому я использовал List в своем коде и добавил ссылки на оба класса в первую строку своего ответа. - person Theo; 02.06.2019
comment
Хотя ArrayList не помечен как [Obsolete()], он устарел, поскольку существует более новая и лучшая альтернатива, которую следует использовать в будущем. Самое первое, что упоминается в вашем ответе, это ArrayList, а затем говорится, что, возможно, List[T] было бы лучше. Я хочу сказать, что List[T] был представлен в .NET 2.0 в 2005 году; зачем вообще упоминать ArrayList вообще для неустаревшего кода в 2019 году? Только мое мнение, но включая закомментированный код и ссылку на документацию для ArrayList, которую Microsoft рекомендует не использовать, представляет ее как разумную эквивалентную альтернативу современному коду, когда это не так. - person Lance U. Matthews; 02.06.2019