PowerShell: производительность Add-Member, Select-Object против ValueFromPipelineByPropertyName – ремейк с развитием
В огромном количестве источников встречал статьи на тему “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 раз.
“О сколько нам открытий чудных…” Именно так…
RSS комментарии
Обратная ссылка