但是线程库甚至需要担心线程分配给内核。这不是操作系统的工作吗?那么使用 TBB 而不是 Boost 的真正好处是什么?
你是对的,线程库通常不应该关心将线程映射到核心。而 TBB 没有。TBB 使用任务而不是线程进行操作。TBB 的调度程序通过分配线程池并让它动态选择要运行的任务来利用所有内核。这是与 Boost 相比的主要优势,您需要手动将可用工作映射到线程。然后 TBB 提供了高级构造,例如 parallel_for、parallel_pipeline 等,可用于表达最常见的并行模式,并隐藏所有与任务的操作。
例如,让我们来看一段计算 Mandelbrot 分形点的代码(取自http://warp.povusers.org/Mandelbrot/,省略了变量初始化):
for(unsigned y=0; y<ImageHeight; ++y)
{
double c_im = MaxIm - y*Im_factor;
for(unsigned x=0; x<ImageWidth; ++x)
{
double c_re = MinRe + x*Re_factor;
double Z_re = c_re, Z_im = c_im;
bool isInside = true;
for(unsigned n=0; n<MaxIterations; ++n)
{
double Z_re2 = Z_re*Z_re, Z_im2 = Z_im*Z_im;
if(Z_re2 + Z_im2 > 4)
{
isInside = false;
break;
}
Z_im = 2*Z_re*Z_im + c_im;
Z_re = Z_re2 - Z_im2 + c_re;
}
if(isInside) { putpixel(x, y); }
}
}
现在要使其与 TBB 并行,您只需将最外层循环转换为 tbb::parallel_for (为简洁起见,我使用 C++11 lambda):
tbb::parallel_for(0, ImageHeight, [=](unsigned y)
{
// the rest of code is exactly the same
double c_im = MaxIm - y*Im_factor;
for(unsigned x=0; x<ImageWidth; ++x)
{
...
// if putpixel() is not thread safe, a lock might be needed
if(isInside) { putpixel(x, y); }
}
});
TBB 将自动将所有循环迭代分布在可用内核上(并且您不必担心多少)并动态平衡负载,这样如果某个线程有更多工作要做,其他线程不仅会等待它,还会提供帮助,最大限度地利用 CPU利用率。尝试用原始线程来实现它,你会感觉到不同:)