Утилизация данных TPL Dataflow

Я работаю над инструментом обработки звука, который хотел бы создать с использованием TPL Dataflow. Сам поток данных будет состоять из аудиосэмплов, передаваемых между блоками обработки звука. Эти образцы обычно имеют размер несколько килобайт (_1 _ / _ 2_ с элементами от 1 до 4 тысяч). Таким образом, поток данных представляет собой простой конвейер, который выглядит следующим образом:

SourceBlock<byte[]> 
    -> TransformBlock<byte[], float[]> 
        -> TransformBlock<float[], float[]>
            -> ...

Некоторые из блоков могут работать чисто «на месте», т. Е. Изменяя входные данные, в то время как другие должны создавать новые сэмплы. Время обработки каждого блока может варьироваться в зависимости от вводимых данных.

Я не хочу постоянно выделять новые массивы и полагаться на сборщик мусора, который позаботится об утилизации объектов. Я хочу получить выгоду от одновременного выполнения блоков и, следовательно, не хочу ограничивать цепочку последовательной обработкой данных (в этом случае мне все равно не понадобится TPL). Мне не нужны блоки обработки для параллельной обработки (меня устраивает не более одного процесса на блок в любой момент времени).

Какая схема является наилучшей для контроля количества образцов в конвейере в данный момент времени и повторного использования образцов / массивов, которые больше не используются?


person Lau Lu    schedule 08.12.2014    source источник
comment
Я не хочу постоянно выделять новые массивы и полагаться на сборщик мусора, который позаботится об утилизации объектов. Почему это требование вашего решения? Влияет ли сборка мусора на вашу производительность ощутимо, и если да, то к какой производительности вы стремитесь?   -  person Jeroen Mostert    schedule 08.12.2014
comment
Я хочу, чтобы этот код можно было запускать на телефонах с использованием xamarin, поэтому объем памяти имеет значение. сложно протестировать все сценарии (особенно аппаратные), поэтому необходимо заранее выявлять и устранять вероятные проблемы   -  person Lau Lu    schedule 08.12.2014
comment
Моя первоначальная мысль заключалась в том, чтобы процессоры производили два данных, один результат обработки и один мусор, который нужно было переработать. последний затем будет направлен на вышестоящие процессоры (фактически создавая петлю в сетке). Не уверен, что это жизнеспособный вариант.   -  person Lau Lu    schedule 08.12.2014


Ответы (1)


Если ваша цель - повторно использовать ваши массивы вместо того, чтобы всегда создавать новые и собирать их сборщиком мусора, вам необходимо использовать ObjectPool:

Шаблон пула объектов - это шаблон проектирования программного обеспечения, который использует набор инициализированных объектов, готовых к использованию, - «пул», вместо того, чтобы выделять и уничтожать их по требованию. Клиент пула запросит объект из пула и выполнит операции с возвращенным объектом. Когда клиент завершает работу, он возвращает объект в пул, а не уничтожает его; это можно сделать вручную или автоматически.

К сожалению, вам, вероятно, придется реализовать это самостоятельно и сделать поточно-ориентированным.

person i3arnon    schedule 08.12.2014
comment
Звучит как хорошее решение. Я думаю, мне следует попытаться оптимизировать переработку на основе запрашивающего потока (например, что делает concurrentBag). Что вы думаете ? - person Lau Lu; 08.12.2014
comment
@LauLu Вы вступаете на территорию микрооптимизации ... знаете ли вы, что блок TPL Dataflow использует выделенный поток для каждого элемента? Я думаю, что BlockingCollection вокруг ConcurrentQueue - это нормально. - person i3arnon; 08.12.2014
comment
Я попробовал ObjectPool, и - как только я понял - он выполняет свою работу, спасибо. С точки зрения производительности это практически не влияет, но сейчас я использую гораздо меньше памяти. - person Lau Lu; 10.12.2014
comment
Ха! TPL Dataflow бесплатно предоставляет нам потокобезопасный пул объектов. BufferBlock<float[]> идеально подходит для этой работы. Поместите в него несколько буферов с помощью blk.Post(new float[4096]), затем вы сможете blk.Receive(), использовать его и отправить обратно, когда закончите с ним. Другие методы на BufferBlock могут привести к еще более красивому пулу объектов, где вы можете асинхронно ждать, пока элемент станет доступным. Как жаль, что вы не заметили его полезности для этого :( - person spender; 21.10.2015
comment
@spender Вам нужен только BufferBlock для асинхронной блокировки (чего вы обычно не делаете в пуле объектов, вы создаете новые элементы). _2 _ / _ 3_ отлично подходят для этого, и это было моим предложением. В любом случае ... использование BufferBlock или другой коллекции - это именно то, что я имел в виду, говоря о создании пула объектов (никто не ожидает, что вы напишете для этого свою собственную примитивную коллекцию). Чего вы не можете (или не должны) делать, так это повторно использовать существующий конвейер потока данных TPL в качестве пула, контролируя количество элементов в конвейере в соответствии с запросом OP. - person i3arnon; 21.10.2015
comment
В самом деле, я предполагал, что BufferBlock использовался изолированно, а не как часть графа потока данных. Я считаю, что BufferBlock настолько удобен, потому что в случае, если BufferBlock пуст, вы можете асинхронно ждать, пока в нем что-то появится. Это может быть очень полезно для ограничения выполнения при нехватке ресурсов. - person spender; 21.10.2015