C++ intel TBB оптимизация внутреннего цикла
Я пытаюсь использовать Intel TBB для распараллеливания внутреннего цикла (2-й из 3-х), однако, я получаю достойную отдачу только тогда, когда внутренние 2-х циклы значительны по размеру.
TBB порождает новые потоки для каждой итерации основного цикла? Есть ли способ уменьшить накладные расходы?
tbb::task_scheduler_init tbb_init(4); //I have 4 cores
tbb::blocked_range<size_t> blk_rng(0, crs_.y_sz, crs_.y_sz/4);
boost::chrono::system_clock::time_point start =boost::chrono::system_clock::now();
for(unsigned i=0; i!=5000; ++i)
{
tbb::parallel_for(blk_rng,
[&](const tbb::blocked_range<size_t>& br)->void
{
:::
Может быть интересно отметить, что у openMP (который я пытаюсь удалить!!!) нет этой проблемы.
Я собираю с:
Intel ICC 12.1 в -03 -xHost -mavx
На intel 2500k (4 ядра)
РЕДАКТИРОВАТЬ: я действительно могу изменить порядок циклов, потому что тест выходных циклов необходимо заменить на предикат, основанный на результате цикла.
1 ответ
Нет, TBB не создает новые потоки для каждого вызова parallel_for
, На самом деле, в отличие от параллельных областей OpenMP, каждый из которых может создавать новую группу потоков, TBB работает с одной и той же группой потоков, пока все task_scheduler_init
объекты уничтожены; и в случае неявной инициализации (с task_scheduler_init
опущено), одни и те же рабочие потоки используются до конца программы.
Так что проблема с производительностью вызвана чем-то другим. Исходя из моего опыта, наиболее вероятные причины:
- отсутствие оптимизации компилятора, в первую очередь автоматическая векторизация (это можно проверить, сравнив однопоточную производительность OpenMP и TBB; если TBB намного медленнее, то это наиболее вероятная причина).
- отсутствует кеш; если вы 5000 раз пробегаете одни и те же данные, локальность кэша имеет огромное значение, и OpenMP по умолчанию
schedule(static)
работает очень хорошо, детерминистически повторяя одно и то же разбиение каждый раз, в то время как планировщик краж работы TBB имеет значительную случайность. Установка размера зерна block_range равным problem_size/num_threads обеспечивает один кусок работы на поток, но не гарантирует одинаковое распределение частей; а такжеaffinity_partitioner
должен помочь с этим.