【问题标题】:OpenMP - how to efficiently synchronize field updateOpenMP - 如何有效地同步字段更新
【发布时间】:2017-03-14 19:23:30
【问题描述】:

我有以下代码:

for (int i = 0; i < veryLargeArraySize; i++){
   int value = A[i];
   if (B[value] < MAX_VALUE) {
     B[value]++;
   }
 }

我想在这里使用 OpenMP 工作共享结构,但我的问题是 B 数组上的同步 - 所有并行线程都可以访问数组 B 的任何元素,该元素非常大(这使得使用锁变得困难,因为我需要太多了)

#pragma omp critical 在这里是一个严重的开销。原子是不可能的,因为if

有人对我如何做到这一点有好的建议吗?

【问题讨论】:

  • BA 相比有多大?
  • @Zulan 都超过100万个元素,A略大(是一张图片的直方图计算——A是像素化的图片,B是直方图)
  • 我会假设直方图的箱显着少于原始数据点的数量。直方图是不是很稀疏?
  • 为什么要截断循环内的计数(B)?为什么不简单地无条件地添加一个,然后在 B 上的单独相位循环中截断这些值。然后你就可以使用原子了。

标签: c optimization parallel-processing synchronization openmp


【解决方案1】:

这是我发现并完成的。

我在一些论坛上读到并行直方图计算通常不是一个好主意,因为它可能比顺序计算更慢且效率更低。

但是,我需要这样做(为了作业),所以我做了以下事情:

  1. 并行处理 A 数组(图像)以确定值的实际范围(直方图 - B 数组) - 找到 A[i] 的 MIN 和 MAX

    int min_value, max_value;
    #pragma omp for reduction(min:min_value), reduction(max:max_value)
    for (i = 0; i < veryLargeArraySize; i++){
         const unsigned int value = A[i];
         if(max_value < value) max_value = value;
         if(min_value > value) min_value = value;
    }
    
    int size_of_histo = max_value - min_value + 1;`
    
  2. 这样,我们可以(可能)将实际直方图大小从例如 1M 元素(在数组 B 中分配)减少到 50K 元素(在 sharedHisto 中分配)
  3. 分配一个共享数组,如:

    int num_threads = omp_get_num_threads();
    int* sharedHisto = (int*) calloc(num_threads * size_of_histo, sizeof(int));
    
  4. 每个线程都被分配了sharedHisto的一部分,并且可以不同步地更新它

    int my_id = omp_get_thread_num();
    #pragma omp parallel for default(shared) private(i)
    for(i = 0; i < veryLargeArraySize; i++){
        int value = A[i];
        // my_id * size_of_histo positions to the begining of this thread's
        // part of sharedHisto .
        // i - min_value positions to the actual histo value
        sharedHisto[my_id * size_of_histo + i - min_value]++;
    }
    
  5. 现在,执行缩减(如此处所述:Reducing on array in OpenMp

    #pragma omp parallel
    {
       // Every thread is in charge for part of the reduced histogram
       // shared_histo with the size: size_of_histo
       int my_id = omp_get_thread_num();
       int num_threads = omp_get_num_threads();
       int chunk = (size_of_histo + num_threads - 1) / num_threads;
       int start = my_id * chunk;
       int end = (start + chunk > histo_dim) ? histo_dim : start + chunk;
    
       #pragma omp for default(shared) private(i, j)
       for(i = start; i < end; i++){
           for(j = 0; j < num_threads; j++){
               int value = B[i + minHistoValue] + sharedHisto[j * size_of_histo + i];
    
               if(value > MAX_VALUE) B[i + min_value] = MAX_VALUE;
               else B[i + min_value] = value;
           }
        }
     }
    

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-09-26
    • 2016-01-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-09-19
    • 1970-01-01
    相关资源
    最近更新 更多