新的运算符+OpenMP动态调度子句
The new operator + OpenMP dynamic schedule clause
我一直在使用C++代码来执行量子化学、原子和分子任务,这意味着要对阵列(1D、2D、3D等)进行大量工作。我有一个完整的类array
来处理这个问题。当然,从一开始,最基本的成员函数就是为这些数组动态分配内存、调整大小或删除它们的函数。
data = new double **[row]();
#pragma omp for schedule(static) nowait
for(unsigned int i = 0; i < row; ++i)
{
data[i] = new double *[column]();
}
现在我正在做的是用OpenMP来加速这些例程。在大多数例程中,我一直在使用schedule(static) nowait
类将循环划分为step/threads
的块,因为这些块在线程处理上花费的时间几乎相同。
但是,对于像上面这样的循环,通过多次调用new
运算符,我有一种(糟糕的)感觉,即这些循环的块在其线程中执行的时间不一样,从某种意义上说,我应该考虑应用schedule(dynamic, chunk_size)
。
你们同意吗?动态分配并不是一项简单的任务,而且可能会很昂贵,因此动态分配块的执行时间可能会有所不同。
事实上,我不确定我是否在堆栈碎片或诸如此类的事情上犯了任何错误。我们欢迎您的建议。
PS.:我使用nowait
子句来尽量减少隐性障碍的瓶颈。
如果使用默认的new
运算符,您的特定循环可能不会提供太多并行机会,因为堆是单个资源,对它的访问需要通过互斥体序列化。然而,假设您希望使用OpenMP的其他循环,以下内容应该会有所帮助。
来自OpenMP 3.1规范:
static当指定schedule(tatic,chunk_size)时,迭代被划分为大小为chunk_size的块,并且这些块按照线程号的顺序以循环方式分配给团队中的线程。
当没有指定chunk_size时,迭代空间被划分为大小大致相等的块,并且每个线程最多分配一个块。请注意,在这种情况下,块的大小是未指定的。
dynamic当指定schedule(dynamic,chunk_size)时,迭代为在线程请求时以块的形式分发给团队中的线程。每个线程执行一个迭代块,然后请求另一个块,直到没有剩余的块要分发。
每个区块都包含chunk_size迭代,但要分发的最后一个区块除外,它可能具有较少的迭代。
如果未指定chunk_size,则默认为1。
在您的情况下,您没有指定chunk_size,因此每个任务的迭代次数是未指定的。
一般来说,我更喜欢对线程数量和每个任务执行的迭代次数进行一些控制。我发现(在使用mingw-w64编译的Windows上),任务启动新的工作块会有很大的开销,所以给它们尽可能大的块是有益的。我倾向于使用动态(尽管我可以对固定执行时间的任务使用静态),并将chunk_size设置为循环计数除以线程数。在您的情况下,如果您怀疑任务执行时间不均衡,可以将其除以2或4。
// At the top of a C++ file:
static int NUM_THREADS = omp_get_num_procs();
// Then for your loop construct (I'm using a combined parallel for here):
#pragma omp parallel for num_threads(NUM_THREADS)
schedule(dynamic, row / NUM_THREADS / 2)
for(unsigned int i = 0; i < row; ++i)
{
data[i] = new double *[column]();
}
请注意,如果不设置num_threads,则默认值将为nthreads var(由omp_get_max_threads
确定)。
关于nowait
子句,显然要确保您没有在循环构造之外使用data
。我在上面使用了一个组合的并行循环结构,这意味着不能指定nowait
。
有一种更干净的方法可以做到这一切:
std::vector<double> actual_data(omp_get_num_procs() * column);
std::vector<double *> data(omp_get_num_procs());
for (unsigned i = 0; i < row; i++) {
data[i] = &(actual_data[i * column]);
}
现在,您有一个一次性分配的单个数组,其中有一个指向该数组的指针数组。在并行算法中,您可以使用data[i][j]
来获得您想要的成员,而开销几乎为零(开销发生在编译时)。
唯一的潜在风险是错误共享,因为矩阵的行可能在其端点共享缓存线。
内存管理是自动的;不需要CCD_ 13任何东西。
如果你计划做很多基本的线性代数运算(BLAS),那么我建议你不要在多维数组中使用数组数组。不幸的是,多维静态和动态数组的C语法破坏了对称性。这导致Java和C#使用相同的语法,因此人们通常认为,当他们分配2D数组时,他们得到的东西与静态数组相同,但来自堆而不是堆栈。您的2D阵列实际上是锯齿形阵列。
根据我在BLAS操作(以及图像处理)方面的经验,您需要用于2D阵列的连续内存块。因此,对于nxn
矩阵,您应该像double[n*n]
一样进行分配,并将其作为data[i*n+j]
进行访问,或者创建一个C++矩阵类,在该类中您可以像matrix.get(i,j)
一样访问矩阵。
不要并行分配内存。无论如何,大多数BLAS操作都将是内存绑定的,因此OpenMP只适用于级别3的BLAS操作,如矩阵乘法、LU因子分解、cholesky分解(称为O(n^3)
)。
- 动态调度到模板函数C++
- openMp 动态调度与按处理时间排序任务时的 LPT 调度相同吗?
- 是否会动态调度在具有 vtable 的类上调用非虚函数
- 带有模板类和动态调度的C 共享_POINTER
- C++或D:在没有动态调度的情况下解耦类的习惯用法
- C++动态调度和后期绑定有什么区别
- 动态调度是如何在程序集中发生的
- 为什么动态调度比 openmp 中的静态调度更快
- Java 和 C++ 中的动态方法调度
- 动态调度在类外部声明的方法
- C++避免使用 using 关键字在派生类中进行动态调度
- OpenMP:循环'std::map'基准测试(动态调度)
- C++中动态调度的规则是什么?
- 我应该在高度动态的系统中使用优先级队列来调度任务(函数等)吗
- O'Reilly "Objective-C Pocket Reference"声称C++不支持动态调度,这是真的吗?
- 动态调度方法
- 针对不相关类型的动态调度的解决方案
- 模板函数的动态调度
- 动态调度哪些函数
- 新的运算符+OpenMP动态调度子句