В огромном количестве источников встречал статьи на тему “Add-Member vs. Select-Object – кто быстрее”. Пишу, естественно, не ради плагиата. У меня часто возникает проблема с преобразованием “интерфейса” входящего объекта, если проще – с переименованием его свойств и, иногда, сменой типов значений. И нашёл более производительный способ решения задачи, о чём и пишу.

Сразу приведу пример кода, на котором проводил сравнение:

$inTemplate = @{ a='1'; b='22'; c='ccccc'; d='dddd'; };
$in = @();
1..1000 | % { $in += ( New-Object PSObject -Property $inTemplate ); };

( Measure-Command {
    foreach ($i in $in)  {
        Select-Object –InputObject $i -Property `
            @{ Name='AA'; Expression={ $_.a } } `
            , @{ Name='BB'; Expression={ $_.b } } `
            , @{ Name='CC'; Expression={ $_.c } } `
            , @{ Name='DD'; Expression={ $_.d } } `
        ;
    }
} ).Milliseconds;

( Measure-Command {
    foreach ($i in $in)  {
        $res = New-Object Object;
        Add-Member NoteProperty AA ( $_.a ) -InputObject $res;
        Add-Member NoteProperty BB ( $_.b ) -InputObject $res;
        Add-Member NoteProperty CC ( $_.c ) -InputObject $res;
        Add-Member NoteProperty DD ( $_.d ) -InputObject $res;
    }
} ).Milliseconds;

( Measure-Command {
        $in `
        | Select-Object -Property `
            @{ Name='AA'; Expression={ $_.a } } `
            , @{ Name='BB'; Expression={ $_.b } } `
            , @{ Name='CC'; Expression={ $_.c } } `
            , @{ Name='DD'; Expression={ $_.d } } `
        ;
} ).Milliseconds;

( Measure-Command {
    $in `
    | % {
        New-Object Object `
        | Add-Member NoteProperty AA ( $_.a ) -passThru `
        | Add-Member NoteProperty BB ( $_.b ) -passThru `
        | Add-Member NoteProperty CC ( $_.c ) -passThru `
        | Add-Member NoteProperty DD ( $_.d );
    }
} ).Milliseconds;

function ConvertTo-NewObject {
    param (
        [Parameter(
            ValueFromPipelineByPropertyName=$true
        )]
        [Alias('a')]
        $AA
    ,
        [Parameter(
            ValueFromPipelineByPropertyName=$true
        )]
        [Alias('b')]
        $BB
    ,
        [Parameter(
            ValueFromPipelineByPropertyName=$true
        )]
        [Alias('c')]
        $CC
    ,
        [Parameter(
            ValueFromPipelineByPropertyName=$true
        )]
        [Alias('d')]
        $DD
    )
    
    New-Object PSObject -Property $PSBoundParameters;
};

( Measure-Command {
    $in `
    | ConvertTo-NewObject `
    ;
} ).Milliseconds;

( Measure-Command {
    foreach ($i in $in)  {
        $i `
        | ConvertTo-NewObject `
        ;
    }
} ).Milliseconds;

Первые три строки кода – подготовка тестового набора данных. Просто строю массив из 1000 объектов с четырьмя свойствами разного типа.

Результаты:

342
533
232
640
98
612

На первых двух вариантах обработки сравниваю производительность Select-Object с Add-Member  конвейере. И в первой, и во второй паре тестов видно, что Select-Object действительно быстрее решает задачу, и будет тем быстрее, чем больше свойств нам требуется обработать. При сравнении первой и второй пары тестов так же видно, что для обработки массы объектов лучше использовать конвейер, а не цикл foreach (1 и 3 тесты).

Последние два теста – уже замеряем “производительность” преобразования объектов в конвейере через механизм связывания параметров фильтров в PowerShell. Результаты неожиданные, и именно они заставили написать эту статью: производительность преобразования свойств объектов в конвейере через механизм привязки параметров фильтрующих функций с использованием псевдонимов –  в 2 раза (и даже более) превосходит самый быстрый до сегодняшнего дня Select-Object! Но – только в конвейере, что и понятно.

Итого – новые выводы в войне за производительность: используйте преобразование и выборку свойств объектов в конвейере через механизм привязки параметров фильтрующих функций с использованием псевдонимов.

Преимущество данного подхода будет более существенным в том случае, когда Вам потребуется добавить некое свойство в результирующий объект только в том случае, если оно есть во входящем. Такого условия с Select-Object уже не построить, а Add-Member с условием проиграет в производительности предлагаемому подходу более, чем в 6 раз.

“О сколько нам открытий чудных…” Именно так…

Опубликовать комментарий

XHTML: Вы можете использовать следующие HTML теги: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

Tags Связь с комментариями статьи:
RSS комментарии
Обратная ссылка