我有一个经过循环的代码块。 代码的一部分对数据向量进行操作,我想把这个操作向量化。 其思想是在多个线程上拆分数组的精化,这些线程将在数组的子部分上工作。 我必须在两种可能性之间作出决定。 第一种方法是在每次遇到此部分时创建线程,并在末尾将它们与主线程重新连接:
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类似,但由于问题很简单,我想使用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,然后再次获取thread_lock以进入睡眠状态,并在它们之间进行同步以通知主线程。
我的问题是,在这样的上下文中,这些解决方案中的哪一种通常是首选的,以及避免的线程创建和销毁开销是否值得增加复杂性(或者,如果增加的同步也会增加时间,那么是否值得)。
显然,这还取决于每个线程的计算时间,但这可能会有很大变化,因为数据向量的长度也可能非常短(每个线程大约两个元素,这将导致大约15毫秒的计算时间)。
创建新线程的最大缺点是线程创建和关闭通常相当昂贵。 与通知条件变量相比,操作系统要做的所有事情都是为了让一个线程离开地面。
请注意,同步始终是必需的,线程创建时也是如此。 实例的C++11std::thread
在构造时引入了与创建线程的synchronizes-with关系。 因此,您可以放心地假设线程创建总是比条件变量信号开销要大得多,而不管您的实现如何。
像OpenMP这样的框架通常会尝试以某种方式摊销这些成本。 例如,OpenMP实现不需要在每次循环后关闭工作线程,而且许多实现不会这样做。