std :: sort比自定义OpenMP并行排序算法快得多

时间:2019-05-10 13:18:55

标签: c++ parallel-processing openmp

我一直在使用OpenMP测试并行排序。我实现了比没有OpenMP快3倍的奇偶排序算法。但是,std :: sort仍然要快得多:seq-100s,并行-20s,std :: sort-0.05s

我尝试将#pragma omp并行移动到i-cycle,但是效果更糟,并且没有对向量进行排序

for (int i = 0; i < 100000; i++) {
        #pragma omp parallel for
        for (int j = (i % 2) ? 0 : 1; j < 100000 - 1; j += 2) {
            if (vec_[j] > vec_[j + 1]) {
                std::swap(vec_[j], vec_[j + 1]);
            }
        }
    }

TBH,我希望并行奇偶排序最快,但现在我想知道-我做错什么了吗,或者只是std :: sort如此有效?

1 个答案:

答案 0 :(得分:5)

您的算法完成的总工作量为O(n ^ 2)。使用k个线程,最多使其变为O(n ^ 2 / k)。

std::sort是O(n lg n)。除非k为O(n / lg n),否则添加更多线程将无法跟上。

即使您没有有很多线程。在大多数大线程系统上,NUMA意味着您的内存将遭受严重的痛苦。线程在每个周期中不会访问相同的内存,并且实际上不断地来回传递数据。

与简单的std :: sort相比,可能加快工作速度的一个示例是将数据拆分为K个片段,std::sort个K个片段中的每一个,然后并行合并这些片段。

int data_count = 100000;
auto get_block_edge = [data_count](int i, int n) {
  return data_count * n / i;
};
int blocks = 0;
#pragma omp parallel
{
  blocks = omp_get_num_threads();
  int block = omp_get_thread_num();
  int start = get_block_edge( block, blocks );
  int finish = get_block_edge( block+1, blocks );
  std::sort( std::begin(vec_)+start, std::begin(vec_)+finish );
}

现在我们有一堆排序块。您只需要合并它们即可。

for (int merge_step = 1; merge_step < blocks; merge_step *= 2 )
{
  #pragma omp parallel for
  for (int i = 0; i < (blocks/2/merge_step); ++i) {
    int start = get_block_edge(i*2*merge_step, blocks);
    int mid = get_block_edge(i*2*merge_step+merge_step, blocks);
    int finish = get_block_edge(i*2*merge_step+2*merge_step, blocks);
    finish = std::min(finish, data_count);
    auto b = std::begin(vec_);
    std::inplace_merge( b+start, b+mid, b+finish );
  }
}

我认为这应该是高度并行的合并。或者更可能是段错误,因为我有一个偏离1的错误。

现在,它仍然是O(n),具有无限数量的线程,因为最后的合并必须是单线程的。轻描淡写地说,解决这个问题很棘手。