【问题标题】:How to use openmp to parallelize the movement of array elements如何使用openmp并行化数组元素的移动
【发布时间】:2016-09-08 00:28:11
【问题描述】:

如果 pArray 非常大,下面的代码段是我的程序中最耗时的。 last 是这个数组结束位置的变量,Idx 是数组特定位置的变量,所以我想要的是将部分数组元素从Idx 向后移动1。

for(long i = last; i >= Idx; i--)
{
    pArray[i] = pArray[i-1];
}

我只是尝试使用 parallel for 来并行化它,但它绝对行不通。谁能告诉我这段代码是否可以与openmp并行化?如果是,如何编码?谢谢。

#pragma omp parallel for
for(long i = last; i >= Idx; i--)
{
    pArray[i] = pArray[i-1];
}

【问题讨论】:

  • 什么是 pArray?也许你可以只使用memcpy(它可以比任何并行化都快)
  • 你想做的有内存依赖。它不能像使用 openmp 一样工作,但是选项 1:您可以尝试临时数组或选项 2:手动划分工作负载(数组),这样不超过 1 个线程可以访问内存位置。
  • 如果您只有 1 个内存控制器,memcpy 应该会提供良好的性能。如果拆分成块,则必须处理源与目标重叠。
  • memcpy 无法处理内存重叠,memmove 可以通过使用临时数组来处理,这意味着这些东西需要复制两次,我将尝试检查性能。
  • memcopy 可以通过自动反转循环来处理重叠。你是对的,memcpy 可能会跳过这一步。

标签: arrays algorithm parallel-processing openmp


【解决方案1】:

您遇到的主要问题是您的代码具有循环携带的依赖关系,即迭代之间存在依赖关系。

所以你的代码是:

for(long i = last; i >= Idx; i--)
{
  pArray[i] = pArray[i-1];
}

现在,假设last = 4Idx=1。你会有类似的东西:

  iteration 0: pArray[4] = pArray[3];
  iteration 1: pArray[3] = pArray[2];
  iteration 2: pArray[2] = pArray[1];
  iteration 3: pArray[1] = pArray[0];

如果你用四个线程并行化(假设是静态的)并且线程 0 被分配迭代 0,线程 1 被分配迭代 1,依此类推,你会得到不正确的结果,具体取决于哪个线程首先执行。如果线程 0 执行 before 线程 1,线程 0 将使用旧值 pArray[3],而如果线程 0 执行 after 线程 1,线程 0 将使用新值为pArray[3],由线程1计算。

由于您的迭代不是独立的,因此循环不能直接并行化。

由于显然您想要的只是将数组的值向前移动一个位置,我认为更好的方法是使用指针算法或重新组织循环和其他代码片段以尝试消除依赖关系或完全循环。

【讨论】:

    【解决方案2】:

    您想要的是并行执行memmove。这是一个可以满足您需求的示例。

    #include <stdio.h>
    #include <string.h>
    #include <omp.h>
    
    #define N 20
    
    int main() {  
        int x[N+1];
        for(int i=0; i<N; i++) x[i] = i+1;
        for(int i=0; i<N; i++) printf("%d ", x[i]); puts("");
        #pragma omp parallel
        {
            int ithread = omp_get_thread_num(), nthreads = omp_get_num_threads();
            size_t pos = ithread*N/nthreads;
            size_t n = (ithread+1)*N/nthreads - pos - 1;
            int tmp = x[pos+n];
            memmove(&x[pos+1], &x[pos], sizeof *x*n);
            #pragma omp barrier
            x[pos+n+1] = tmp;   
        }
        for(int i=0; i<N+1; i++) printf("%d ", x[i]); puts("");
    }
    

    【讨论】:

      【解决方案3】:

      您可以将数组分成多个块。 所以每个线程只能在他的块上工作。

      例如,如果您的数组大小为 1000,并且您有 4 个线程,那么第一个线程将移动块 1-198 中的值,第二个线程将移动块 201-398 中的值,依此类推。 不要忘记在边界上移动值的特殊情况(例如 200 到 199)

      您不需要创建“平行于”,只有“平行”区域才有用。

      【讨论】:

      • 在这种情况下,您需要parallel for(如果不存在依赖项)。如果您只使用parallel,则该区域中的每个线程将执行所有循环的迭代,而不是将迭代分布在线程之间。
      • 我基本上实现了你所说的文字(虽然我在详细阅读你的答案之前做到了)。
      猜你喜欢
      • 2015-05-30
      • 1970-01-01
      • 2015-01-19
      • 1970-01-01
      • 2013-09-19
      • 1970-01-01
      • 1970-01-01
      • 2020-04-24
      相关资源
      最近更新 更多