为什么CILK_NWORKERS仅影响一个cilk_spawn的程序?

时间:2014-09-23 20:11:46

标签: c multithreading parallel-processing cilk cilk-plus

我正试图对矩阵处理程序进行并列化。使用OpenMP后我决定查看CilkPlus,我注意到以下内容:

在我的C代码中,我只在一个部分中应用并行性,即:

//(test_function declarations)

cilk_spawn highPrep(d, x, half);

d = temp_0;
r = malloc(sizeof(int)*(half));
temp_1 = r;
x = x_alloc + F_EXTPAD;
lowPrep(r, d, x, half);

cilk_sync;

//test_function return

根据我到目前为止所阅读的文档,cilk_spawn应该是-maybe-(CilkPlus不强制并行)获取highPrep()函数并在一个可用的不同硬件线程中执行它,然后继续执行其余代码,包括函数lowPrep()。然后,线程应该在cilk_sync上同步,然后执行代码的其余部分。

我在8核/ 16线程Xeon E5-2680上运行它,除了我的实验之外,在任何给定时间都没有执行任何其他操作。我在这一点上的问题是,我注意到当我更改环境变量CILK_NWORKERS并尝试诸如2,4,8,16之类的值时,test_function需要执行的时间会发生很大变化。特别是,设置的CILK_NWORKERS越高(2之后),函数变得越慢。这似乎与我相反,因为我希望可用的线程数不会改变cilk_spawn的操作。我希望如果2个线程可用,那么函数highPrep将在另一个线程上执行。任何超过2个线程我都希望保持空闲状态。

highPrep和lowPrep函数是:

void lowPrep(int *dest, int *src1, int *src2, int size)
{
   double temp;
   int i;
   for(i = 0; i < size; i++)
   {
      temp = -.25 * (src1[i] + src1[i + 1]) + .5;
      if (temp > 0)
         temp = (int)temp;
      else
      {
          if (temp != (int)temp)
              temp = (int)(temp - 1);
      }
      dest[i] = src2[2*i] - (int)(temp);
   }
}

void highPrep(int *dest, int *src, int size)
{
   double temp;
   int i;
   for(i=0; i < size + 1; i++)
   {
      temp = (-1.0/16 * (src[-4 + 2*i] + src[2 + 2*i]) + 9.0/16 * (src[-2 + 2*i] + src[0 + 2*i]) + 0.5);
      if (temp > 0)
           temp = (int)temp;
      else
      {
         if (temp != (int)temp)
              temp = (int)(temp - 1);
      }
      dest[i] = src[-1 + 2*i] - (int)temp;
    }
}

这背后一定有合理的解释,期望这样的程序有不同的执行时间是否合理?

1 个答案:

答案 0 :(得分:1)

澄清:Cilk确实&#34;继续偷窃&#34;而不是&#34;偷窃&#34;,所以highPrep总是在与其调用者相同的硬件线程上运行。它是代码的其余部分&#34;最终可能会在另一个线程上运行。有关更全面的解释,请参阅this primer

关于减速,它可能是实现的工件偏向于可以消耗所有线程的高度并行性。额外的线程正在寻找工作,并且在这样做的过程中,占用一些内存带宽,而对于超线程处理器来说会占用一些核心周期。 Linux&#34;完全公平的调度程序&#34;在这个领域给了我们一些悲伤,因为睡眠(0)不再放弃时间片。额外的线程也可能导致操作系统将软件线程映射到机器上的效率降低。

问题的根源是一个棘手的权衡:运行窃贼积极地使他们能够更快地获得工作,但如果没有可用的工作,也会导致他们不必要地消耗资源。当没有可用的工作时让盗贼进入睡眠状态会节省资源,但会增加产生的大量开销,因为现在产生的线程必须检查是否有睡眠线程被唤醒。 TBB支付了这笔费用,但TBB并不多,因为无论如何TBB的产生开销要高得多。当前的Cilk实现确实支付了这个税:它只在顺序执行期间让工人睡觉。

我能给出的最好(但不完美)的建议是找到更多的并行性,这样就不会有工作线程长时间游荡并造成麻烦。