Высокое потребление памяти при возврате байтового массива из функции - PullRequest
4 голосов
/ 07 июля 2019

Я пытаюсь загрузить файл размером 10 МБ и сохранить его в виде массива для дальнейшей обработки.

Все выглядит нормально при использовании прямого звонка на (New-Object System.Net.WebClient).DownloadData("<url>"). Но если я оберну его внутри функции и верну результат вызова WebClient::DownloadData объем памяти увеличится примерно до 500 МБ.

Функция, которую я использую:

function My-Download {
    param (
        [Parameter(Mandatory = $True, Position = 1)] [String] $UrlCode
    )
    (New-Object System.Net.WebClient).DownloadData($UrlCode)
}
$x = My-Download("https://file-examples.com/wp-content/uploads/2017/04/file_example_MP4_1280_10MG.mp4")

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

Вызов $x = (New-Object System.Net.WebClient).DownloadData("https://file-examples.com/wp-content/uploads/2017/04/file_example_MP4_1280_10MG.mp4") приводит к 83 МБ:

direct call memory consumption

Вызов вышеуказанной функции приводит к 500 МБ:

wrapper function memory consumption

В чем причина такого высокого использования памяти и что я могу сделать, чтобы оптимизировать его?

Версия Powershell:

Major  Minor  Build  Revision
-----  -----  -----  --------
5      1      17134  407

1 Ответ

1 голос
/ 08 июля 2019

Метод .DownloadData() типа [System.Net.WebClient] возвращает байтовый массив ([byte[]]).

  • Если вы присваиваете результат вызова этого метода переменной напрямую , переменная получает этот байтовый массив как есть .

  • Напротив, , если вызов этого метода используется для получения неявного вывода из функции , [byte[]] Элементы массива отправляются в конвейер, один за другим (байт за байтом).
    Целью проекта в конвейере является включение потоковой , объект-за-обработкой обработки, а не поведения «собирать все результаты в первую очередь», которое меняет скорость выполнения для регулирования памяти, один один к одному, обработка по мере поступления становится доступной.

Присваивание переменной функции переменной затем заставляет PowerShell неявно собирать отдельные выходные объекты (в данном случае байты) в обычный массив [object[]].

Другими словами: исходный массив [byte[]] был первым , перечисляемым , только для того, чтобы быть собранным позже в другом массиве , хотя и [object[]] -типного - это, очевидно, ненужный и неэффективный в вашем сценарии.

Существует два способа отказаться от этого неявного перечисления :

  • Вместо неявного вывода можно использовать концептуально явный вызов Write-Output -NoEnumerate, чтобы подавить перечисление выходного массива ( сбор):

    • Write-Output -NoEnumerate (New-Object System.Net.WebClient).DownloadData($UrlCode)
  • Более неясный, но более краткий и быстрый альтернатива состоит в объединении неявного вывода с вспомогательным одноэлементным массивом-оберткой , что заставляет PowerShell перечислять только массив-обертку, пропуская обернутый массив через, как PetSerAl предлагает в комментарии к вопросу:

    • , (New-Object System.Net.WebClient).DownloadData($UrlCode)

    • , является оператором построения массивов PowerShell ( "оператор запятой" ), и в своей унарной форме он объединяет RHS в один элемент ( [object[]]) массив.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...