OpenMP 任务中的数据属性
Data Attribute in OpenMP's task
我已经用OpenMP编写了一个与快速排序相关的代码,如下所示:
#include <iostream>
#include <ctime>
#include <algorithm>
#include <functional>
#include <cmath>
using namespace std;
#include <omp.h>
void ParallelQuickSort(int *begin, int *end)
{
if (begin+1 < end)
{
--end;
int *middle = partition(begin, end, bind2nd(less<int>(), *end));
swap(*end, *middle);
#pragma omp task shared(begin) firstprivate(middle)
ParallelQuickSort(begin, middle);
#pragma omp task shared(end) firstprivate(middle)
ParallelQuickSort(++middle, ++end);
}
}
int main()
{
int n = 200000000;
int* a = new int[n];
for (int i=0; i<n; ++i)
{
a[i] = i;
}
random_shuffle(a, a+n);
cout<<"Sorting "<<n<<" integers."<<endl;
double startTime = omp_get_wtime();
#pragma omp parallel
{
#pragma omp single
ParallelQuickSort(a, a+n);
}
cout<<omp_get_wtime() - startTime<<" seconds."<<endl;
for (int i=0; i<n; ++i)
{
if (a[i] != i)
{
cout<<"Sort failed at location i="<<i<<endl;
}
}
delete[] a;
return 0;
}
我在代码中遇到的问题是ParallelQuickSort
函数中任务构造中的数据属性。变量middle应该是firstprivate
而不是shared
,因为执行这两个任务的线程可能会更改它。但是,如果我如代码中所示将变量begin和end设置为shared
,则程序将失败。我想知道为什么它们(begin
和end
)应该是firstprivate
而不是shared
。在我看来,由于执行这两个任务的线程分别保留变量begin
和end
,因此它们不会相互影响。另一方面,ParallelQuickSort
函数是递归的,因此在变量begin
或end
中存在竞争(例如,在父函数和子函数中)。我不确定这个怀疑,因为变量在不同的函数(父函数和子函数)中。
首先,在显式任务中,在给定区域中被确定为private
的变量自动为firstprivate
,因此不需要将它们显式声明为firstprivate
。其次,您的代码包含++end;
和--end;
,它们修改了end
的值,如果end
是shared
,则会影响其他任务。firstprivate
在这里是正确的数据共享类——每个任务只保留创建任务时它们曾经具有的begin
、end
和middle
的值。
您的ParallelQuickSort
应该如此简单:
void ParallelQuickSort(int *begin, int *end)
{
if (begin+1 < end)
{
--end;
int *middle = partition(begin, end, bind2nd(less<int>(), *end));
swap(*end, *middle);
#pragma omp task
ParallelQuickSort(begin, middle);
#pragma omp task
ParallelQuickSort(++middle, ++end);
}
}
请注意,尽管此代码有效,但它比单线程版本慢得多:在大型Xeon X7350(Tigerton)机箱上使用2个线程需要88.2秒,而使用单个线程需要50.1秒。原因是任务的创建一直持续到交换两个数组元素这一非常简单的任务。使用任务的开销是巨大的,应该设置一个合理的上限,低于该上限应该禁用任务,比如说当子数组大小达到1024个元素时。确切的数字取决于OpenMP运行时实现、CPU类型和内存速度,因此1024的值或多或少是随机选择的。尽管如此,最优值不应该创建两个处理落在同一缓存行中的元素的任务,因此元素的数量应该是16的倍数(每个缓存行64个字节/4个整数字节):
void ParallelQuickSort(int *begin, int *end)
{
if (begin+1 < end)
{
--end;
int *middle = partition(begin, end, bind2nd(less<int>(), *end));
swap(*end, *middle);
#pragma omp task if((end - begin) > 1024)
ParallelQuickSort(begin, middle);
#pragma omp task if((end - begin) > 1024)
ParallelQuickSort(++middle, ++end);
}
}
通过此修改,代码在同一个盒子上运行两个线程,持续34.2秒。
- Pisarze - 来自波兰奥林匹克信息学的数据分析任务
- 我应该使用多个角色还是一个角色,将实际属性推迟到将数据包装/公开为其属性的QObject
- 具有相似属性的不同数据类型的模板
- 从高度动态的C 数据模型中更新QML:计时器与属性绑定
- 如何从C 中的HDF5文件中读取属性名称和数据集名称,在这里我不知道HDF5文件中存在什么属性
- 我如何使用GO语言读取可能是两种不同数据类型之一的HDF5属性
- 具有数据成员语法的零成本属性
- 新任务中以前的数据会发生什么变化
- 哪个更快,单个对象数组或多个数据属性数组
- 只有Arduino的第一个对象在包含在另一个对象的集合/数组中时会丢失其数据属性值
- OpenMP中类成员变量的数据共享属性
- OpenMP 任务中的数据属性
- SQLite是否可以完成此任务?存储路径名与文件属性
- 以下数据处理任务是否适合GPU计算
- 限制数据类型属性冲突-ODBC with Access Database
- 如何将复杂的指针数据作为属性放入qt-plugin-xml文件中
- C++-填充派生类数据成员,同时设置基类属性值
- 更改boost属性树的数据类型
- 在构造子类对象的过程中,更改一个类属性的数据类型会导致segfault
- 对相关数据执行任务级并行性