2014-09-30 3 views
3

Я использую Concurrency::parallel_for() библиотеки параллельных шаблонов Visual Studio 2010 (PPL) для обработки индексированного набора задач (обычно набор индексов намного больше, чем число потоки, которые могут выполняться одновременно). Каждая задача, прежде чем делать длительный расчет, начинается с запроса отдельного рабочего ресурса хранения из общего диспетчера ресурсов (в случае: вид на файл с привязкой к конкретным задачам, но я думаю, что сюжетная линия будет одинаковой, если каждая задача запросил выделение частной памяти из общей кучи).Параллелизм :: parallel_for (PPL) создает слишком много потоков

Использование диспетчера разделяемых ресурсов синхронизируется с Concurrency::critical_section, и здесь возникает проблема: если первый поток/задача находится в критическом разделе, а вторая задача выполняет запрос, ему приходится ждать запроса первой задачи обрабатывается. Очевидно, что PPL думает: эй этот поток ждет, и есть больше задач, поэтому создается другой поток, вызывающий до 870 потоков, которые в основном ждут того же менеджера ресурсов.

Теперь, когда обработка запроса ресурса является лишь малой частью всей задачи, я хотел бы сказать, что PPL в этой части удерживает своих лошадей, ни один из ожидающих или кооперативных блоков не должен заставлять новые потоки начинать с указанный раздел рабочей нити, и мой вопрос здесь: , если и как я могу предотвратить, чтобы определенная секция потока создавала новые потоки, даже если она взаимодействует с блоками. Я бы не прочь создать новые потоки на других блоках дальше по пути обработки потока, но не более чем на 2 * количество (гипер) ядер.

Альтернатива, которые я до сих пор считаюсь:

  1. очередь вверх задача и обрабатывать очередь из ограниченного числа потоков. Проблема. Я надеялся, что PPL parallel_for сделает это сам по себе.

  2. Определить Concurrency::combinable<Resource> resourceSet; за пределами Concurrency::parallel_for и инициализировать resourceSet.local() один раз, чтобы уменьшить количество запросов ресурсов (путем повторного использования ресурсов) до количества потоков (что должно быть меньше количества задач). Проблема: эта оптимизация не предотвращает создание лишней нити.

  3. Предварительно выделяйте необходимые ресурсы для каждой задачи вне цикла parallel_for. Проблема: это потребует слишком много системных ресурсов, тогда как ограничение количества ресурсов на количество потоков/ядер будет в порядке (если это не взорвалось).

Я прочитал http://msdn.microsoft.com/en-us/library/ff601930.aspx, раздел «Не блокируйте Многократно в параллельном цикле», но следуя советам здесь приведет не параллельных потоков вообще.

+0

Я только что нашел HTTP : //stackoverflow.com/questions/9990363/thread-ids-with-ppl-and-parallel-memory-allocation? rq = 1, который очень похож на этот вопрос. –

ответ

3

Я не знаю, можно ли настроить PPL/ConcRT, чтобы не использовать совместную синхронизацию или, по крайней мере, поставить ограничение на количество создаваемых им потоков. Я думал, что это можно контролировать через scheduler policies, но, похоже, ни один из параметров политики не подходит для этой цели.

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

  • Вместо critical_section используйте некооперативную примитив синхронизации для защиты менеджер ресурсов. Я думаю (хотя не проверял), что классический WinAPI CRITICAL_SECTION должен преуспеть. В качестве радикального шага в этом направлении вы можете рассмотреть другие параллельные библиотеки для своего кода; например Intel's TBB provides most of PPL API and has more (отказ от ответственности: я связан с ним).

  • Предварительно выделите несколько ресурсов за пределами параллельного цикла. Один ресурс для каждой задачи не требуется; один на поток должен быть достаточным. Поместите эти ресурсы в concurrent_queue, и внутри задачи вытащите ресурс из очереди, используйте, а затем отпустите его. Кроме того, вместо того, чтобы возвращать ресурс в очередь, поток может накапливать его внутри объекта combinable для повторного использования в других задачах. Если очередь оказывается пустой (например, если PPL переписывает устройство), могут быть разные подходы, например. вращение в цикле до тех пор, пока какой-либо другой поток не вернет ресурс или не запросит другой ресурс у менеджера. Также вы можете предварительно выделить больше ресурсов, чем количество потоков, чтобы минимизировать шансы на исчерпание ресурсов.

+0

Спасибо, я рассмотрю и протестирую несотрудническую синхронизацию (хотя мой менеджер ресурсов также используется в других контекстах, в которых требуется совместная синхронизация), TBB, а также boost :: thread. –

+0

Я думаю, что всплывающие ресурсы из очереди внутри задачи также запускают новые потоки, когда очередь пуста и (совместно) блокировка, и другие задачи все еще ждут начала. –

+0

'concurrent_queue' не имеет метода блокировки pop, только' try_pop() ', который немедленно возвращается, если очередь пуста (см. Http://msdn.microsoft.com/en-us/library/ee355358.aspx). Таким образом, вы можете, например, сделайте цикл вращения, который вызывает 'try_pop()' до достижения успеха. –

1

Мой ответ не «» решение с использованием PPL, но я думаю, что вы могли бы сделать это так легко с пулом потоков, как taskqueue, вы должны взглянуть на this answer.

Так вы заполните очередь с вашими работами, он гарантирует, что не будет больше, чем «х» задач, работающих параллельно, где х boost::thread::hardware_concurrency() (да увеличить еще раз ...)

+0

Спасибо, я еще раз посмотрю на boost :: threads, хотя у него, похоже, нет parallel_for, поэтому мне пришлось бы разбить задачу, заданную в ограниченном наборе диапазонов задач. –