【问题标题】:Parallelization of true dependencies真正依赖的并行化
【发布时间】:2020-02-23 10:30:00
【问题描述】:

是否值得并行化真正的依赖循环?有什么好处和坏处?我们平均可以获得多少加速?

例如:

    int sum = 0;
    for(i=0;i<2000-1;i++){
            for(j=0;j<2000;j++) {
                curr[i][j] = some_value_here;
                sum += curr[i][j];
            }
    }

我应该如何处理这个循环?有一个明显的 RAW 依赖关系,我应该并行化它吗?如果是,我应该怎么做?

【问题讨论】:

  • 这个问题的答案在于我对你另一个问题的回答。
  • 哪一个,哈哈!
  • “真正的”依赖是什么意思?存在明显的 read-after-write 依赖性; sum 在每次迭代中写入并在下一次读取。在不了解硬件特性的情况下,无法说明可以获得多少加速。所写的代码执行了 3,998,000 次加法的链,但它的依赖深度可以减少到 ceil(log[2](3,998,000−1)) = 22,因此如果您单独加法的潜在加速超过 181,727有它的硬件。
  • @EricPostpischil,这实际上是一个简单的归约问题,加速比大致等于执行单元的数量。
  • @HristoIliev:正如我所写,这取决于硬件。这个问题询问我们平均可以得到多少加速,在没有硬件规范的情况下,这不是一个明智的问题。

标签: c loops parallel-processing dependencies openmp


【解决方案1】:

sum 充当简单的累加器,整个操作是并行归约。正确的解决方案是让每个线程累积自己的私有总和,然后在最后将所有私有总和相加。 OpenMP 提供的 reduction 子句正是这样做的:

int sum = 0;
#pragma omp parallel for collapse(2) reduction(+:sum)
for(i=0;i<2000-1;i++){
        for(j=0;j<2000;j++) {
            curr[i][j] = some_value_here;
            sum += curr[i][j];
        }
}

reduction(+:sum) 告诉编译器创建sum 的私有副本,然后应用+ 运算符将这些私有副本减少为单个值,然后将其添加到区域之前的sum 的值。代码大致相当于:

int sum = 0;
#pragma omp parallel
{
   int localsum = 0;
   #pragma omp for collapse(2)
   for(i=0;i<2000-1;i++) {
      for(j=0;j<2000;j++) {
         curr[i][j] = some_value_here;
         localsum += curr[i][j];
      }
   }
   #pragma omp atomic
   sum += localsum;
}

这里的潜在加速等于执行单元的数量,前提是每个执行单元有一个线程并且没有那么多线程,因此并行区域末尾的同步求和所需的时间可以忽略不计。

【讨论】:

  • 我用16和32线程来测试,你觉得你用原子操作的方式是不是一个不错的选择?
  • 不是说要使用atomic - 它只是为了说明当您应用reduction 子句时OpenMP 为您做了什么。
猜你喜欢
  • 1970-01-01
  • 2020-04-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-07-14
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多