std::sort 比自定义 OpenMP 并行排序算法快得多
std::sort is way more fast than custom OpenMP parallel sorting algorithm
我一直在使用 OpenMP 测试并行排序。我实现了比没有 OpenMP 快 3 倍的奇偶排序算法。然而,std::sort 仍然更快:seq - 100s,parallel - 20s,std::sort - 0.05s
我尝试将#pragma omp parallel for 移动到 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]);
}
}
}
老实说,我原以为并行奇偶排序是最快的,但现在我想知道 - 我做错了什么还是只是 std::sort 如此高效?
您的算法完成的总工作量为 O(n^2)。使用 k 个线程,这最多使它成为 O(n^2/k).
std::sort
是 O(n lg n)。除非 k 是 O( n / lg n ) 添加更多线程不会跟上。
即使你确实有一堆线程。大多数 mega-thread 系统上的 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 );
}
}
我认为这应该是一个高度并行的合并。或者,更有可能是段错误,因为我有一个 off-by-1 错误。
现在,这仍然是具有无限数量线程的 O(n),因为最后一次合并必须是 single-threaded。委婉地说,绕过这个问题很棘手。
我一直在使用 OpenMP 测试并行排序。我实现了比没有 OpenMP 快 3 倍的奇偶排序算法。然而,std::sort 仍然更快:seq - 100s,parallel - 20s,std::sort - 0.05s
我尝试将#pragma omp parallel for 移动到 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]);
}
}
}
老实说,我原以为并行奇偶排序是最快的,但现在我想知道 - 我做错了什么还是只是 std::sort 如此高效?
您的算法完成的总工作量为 O(n^2)。使用 k 个线程,这最多使它成为 O(n^2/k).
std::sort
是 O(n lg n)。除非 k 是 O( n / lg n ) 添加更多线程不会跟上。
即使你确实有一堆线程。大多数 mega-thread 系统上的 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 );
}
}
我认为这应该是一个高度并行的合并。或者,更有可能是段错误,因为我有一个 off-by-1 错误。
现在,这仍然是具有无限数量线程的 O(n),因为最后一次合并必须是 single-threaded。委婉地说,绕过这个问题很棘手。