Условные переменные C ++ против новых потоков для векторизации - PullRequest
0 голосов
/ 02 августа 2020

У меня есть блок кода, который проходит через al oop. Часть кода работает с вектором данных, и я хотел бы векторизовать эту операцию. Идея состоит в том, чтобы разделить обработку массива на несколько потоков, которые будут работать с частями массива. Я должен выбрать между двумя возможностями. Первый - создавать потоки каждый раз, когда встречается этот раздел, и повторно объединять их в конце с основным потоком:

for(....)
{
//serial stuff

//crate threads
for(i = 0; i < num_threads; ++i)
{
    threads_vect.push_back(std::thread(f, sub_array[i]));
}

//join them
for(auto& t : threads_vect)
{
    t.join();
}

//serial stuff
}

Это похоже на то, что делается с OpenMP, но поскольку проблема заключается в simple Я бы хотел использовать std :: threads вместо OpenMP (если против этого нет веских причин).

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

std::condition_variable cv_threads;
std::condition_variable cv_main;

//create threads, the will be to sleep on cv_threads

for(....)
{
//serial stuff

//wake up threads
cv_threads.notify_all();

//sleep until the last thread finishes, that will notify.
main_thread_lock.lock();
cv_main.wait(main_lock);

//serial stuff
}

Чтобы обеспечить параллелизм, потоки должны будут разблокировать thread_lock, как только они проснутся в начале вычисления, затем снова получите его в go, чтобы засыпать и синхронизировать между ними, чтобы уведомить основной поток.

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

Очевидно, это также зависит от того, как долго выполняется вычисление для каждого потока, но это может сильно варьироваться, поскольку длина вектора данных также может быть очень коротким (примерно до двух элементов на поток, что привело бы к времени вычисления около 15 миллисекунд).

1 Ответ

1 голос
/ 02 августа 2020

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

Обратите внимание, что синхронизация всегда требуется, также с созданием потока. В C ++ 11 std::thread для экземпляров вводится связь синхронизируется с с создаваемым потоком при построении. Таким образом, вы можете с уверенностью предположить, что создание потока всегда будет значительно дороже, чем сигнализация переменных условий, независимо от вашей реализации.

Фреймворк, подобный OpenMP, обычно пытается каким-то образом компенсировать эти затраты. Например, реализации OpenMP не требуется завершать рабочие потоки после каждого l oop, и многие реализации этого не делают.

...