- Java 双重比较
- java - 比较器与 Apache BeanComparator
- Objective-C 完成 block 导致额外的方法调用?
- database - RESTful URI 是否应该公开数据库主键?
我目前正在尝试提高我的代码的并行性能,但我对 OpenMP 还是个新手。我必须遍历一个大容器,在每次迭代中读取多个条目并将结果写入单个条目。下面是我正在尝试做的一个非常简单的代码示例。
data
是一个指向数组的指针,数组中存储了很多数据点。在并行区域之前,我创建了一个数组 newData
,因此可以将 data
用作只读,将 newData
用作只写,之后我抛出旧的 data
并使用 newData
进行进一步的计算。据我了解,data
和 newData
在线程之间共享,并且在并行区域内声明的所有内容都是私有(private)的。多线程读取数据
会导致性能问题吗?
我正在使用 #critical
为 newData
的元素分配新值以避免竞争条件。这是必要的吗,因为我只访问了 newData
的每个元素一次,而且从不通过多个线程访问?
我也不确定时间安排。我是否必须指定是要static
还是dynamic
时间表?我可以使用 nowait
因为所有线程都是相互独立的吗?
array *newData = new array;
omp_set_num_threads (threads);
#pragma omp parallel
{
#pragma omp for
for (int i = 0; i < range; i++)
{
double middle = (*data)[i];
double previous = (*data)[i-1];
double next = (*data)[i+1];
double new_value = (previous + middle + next) / 3.0;
#pragma omp critical(assignment)
(*newData)[i] = new_value;
}
}
delete data;
data = newData;
我知道在第一次和最后一次迭代中 previous
和 next
不能从 data
中读取,在真实代码中这是已处理,但对于这个最小的示例,您会想到从 data
中多次读取。
最佳答案
首先,摆脱所有不必要的依赖。 #pragma omp critical(assignment)
不是必需的,因为 (*newData)
的每个索引在每个循环中只写入一次,因此不存在竞争条件。
你的代码现在看起来像这样:
#pragma omp parallel for
for (int i = 0; i < range; i++)
(*newData)[i] = ((*data)[i-1] + (*data)[i] + (*data)[i+1]) / 3.0;
现在我们正在寻找瓶颈。我想出的潜在候选人名单是这样的:
那么让我们进一步分析它们。
慢除法:计算 double/double 需要一些 CPU 永远。要了解您的 CPU 的运行时间和吞吐量,您必须查看其规范。也许将 /3.0
替换为 *0.3333..
可能会有所帮助,但也许您的编译器已经这样做了。使用扩展指令集(如 SSE/AVX),您可以一次安排多个除法/乘法。
缓存抖动:因为您的 CPU 必须一次加载/存储一个缓存行,所以可能会发生冲突。想象一下,如果线程 1 尝试写入 (*newdata)[1],而线程 2 尝试写入 (*newdata)[2],并且它们位于同一缓存行上。现在他们中的一个必须等待另一个。您可以使用 #pragma omp parallel for schedule(static, 64)
解决此问题。
ILP:如果操作是独立的,CPU 可以将多个操作安排到管道中。为此,您必须展开循环。这可能看起来像这样:
assert(range % 4 == 0);
#pragma omp parallel for
for (int i = 0; i < range/4; i++) {
(*newData)[i*4+0] = ((*data)[i*4-1] + (*data)[i*4+0] + (*data)[i*4+1]) / 3.0;
(*newData)[i*4+1] = ((*data)[i*4+0] + (*data)[i*4+1] + (*data)[i*4+2]) / 3.0;
(*newData)[i*4+2] = ((*data)[i*4+1] + (*data)[i*4+2] + (*data)[i*4+3]) / 3.0;
(*newData)[i*4+3] = ((*data)[i*4+2] + (*data)[i*4+3] + (*data)[i*4+4]) / 3.0;
}
内存带宽限制:对于您非常简单的循环,请考虑一下。您需要加载多少内存以及您的 CPU 将忙于处理它多长时间。您正在加载大约 1 个缓存行并计算一些取消引用、一些指针加法、两次加法和一次除法。您达到的限制取决于您的 CPU 规范。现在考虑缓存局部性。您可以修改代码以更好地利用缓存吗?如果一个线程在一个循环迭代中获得 i=3,而在下一个循环迭代中获得 i=7,则您必须重新加载 3 个(*数据)。但是如果你从 i=3 到 i=4,你可能不需要加载任何东西,因为 (*data)[i+1] 在之前加载的缓存行中。您节省了一些 RAM 带宽。要利用它,展开循环。使用 float 而不是 double 也会增加这种机会。
隐藏的依赖项:现在这部分我个人觉得非常棘手。有时您的编译器不确定它是否可以重用某些数据,因为它不知道它没有改变。使用 const
有助于编译器。但有时您需要 restrict
来为编译器提供正确的提示。但我不太了解这一点,无法解释。
所以这是我会尝试的:
const double ONETHIRD = 1.0 / 3.0;
assert(range % 4 == 0);
#pragma omp parallel for schedule(static, 1024)
for (int i = 0; i < range/4; i++) {
(*newData)[i*4+0] = ((*data)[i*4-1] + (*data)[i*4+0] + (*data)[i*4+1]) * ONETHIRD;
(*newData)[i*4+1] = ((*data)[i*4+0] + (*data)[i*4+1] + (*data)[i*4+2]) * ONETHIRD;
(*newData)[i*4+2] = ((*data)[i*4+1] + (*data)[i*4+2] + (*data)[i*4+3]) * ONETHIRD;
(*newData)[i*4+3] = ((*data)[i*4+2] + (*data)[i*4+3] + (*data)[i*4+4]) * ONETHIRD;
}
然后是基准测试。多做一些基准测试,多做一些基准测试。只有基准测试会告诉您哪些技巧有用。
PS:还有一件事需要考虑。如果您发现您的程序严重占用了内存带宽。您可以考虑更改算法。也许将两步合二为一。就像从b[i] := (a[i-1] + a[i] + a[i+1])/3.0
到d[i] := (n[i-1] + n[i] + n[i+1])/3.0 = (a[i-2] + 2.0 * a[i-1] + 3.0 * a[i] + 2.0 * a[i+1] + a[i+1])/3.0
。我想您会自己找出原因。
享受优化的乐趣 ;-)
关于c++ - 如何提高我的 OpenMP 代码的性能?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39751294/
OpenMP 中的高斯消除。我是 openmp 的新手,想知道我是否在正确的地方使用了我的编译指示和屏障。我的 x 值每次都不同。他们应该是一样的吗?? #include int num; doub
给定一个示例函数(示例在下面给出),for 循环可以使用 OpenMP 并行化或使用矢量化进行矢量化(假设编译器执行矢量化)。 示例 void function(float* a, float* b,
OpenMP 中原子和关键之间有什么区别? 我能做到 #pragma omp atomic g_qCount++; 但这和不一样吗 #pragma omp critical g_qCount++; ?
我有一个关于如何在您考虑特定依赖关系图时生成 OpenMP 伪代码的问题。 所以假设我们有这个特定的图表: 解决方案可能是这样的: #pragma omp parallel {
我正在尝试使用 openmp 计算二维矩阵的平均值。这个二维矩阵实际上是一个图像。 我正在对数据进行线程分割。例如,如果我有 N线程比我处理行/N thread0 的行数, 等等。 我的问题是:我可以
我想统计测量与 OpenMP 并行化的程序的性能。我选择在执行并行算法的测试应用程序中编写循环 MAX_EXPERIMENTS次并将时间测量报告到文件中。 问题解决方案似乎比提取外部循环上方的并行编译
我找到了 Intel's performance suggestion on Xeon Phi关于 OpenMP 中的 Collapse 子句。 #pragma omp parallel for co
如何使用 OpenMP 并行化数组移位? 我尝试了一些方法,但在以下示例中没有得到任何准确的结果(该示例旋转 Carteira 对象数组的元素,用于排列算法): void rotaciona(int
我有一系列对几个独立函数的调用。 func1(arg); func2(arg); func3(arg); 我想并行执行它们,而不是串行执行它们。我目前正在使用 #pragma omp parallel
我正在尝试使用 openmp 任务来安排基本 jacobi2d 计算的平铺执行。在 jacobi2d 中,依赖于 A(i,j) 从 A(i, j) A(i-1, j) A(i+1, j) A(i, j
我在 3 天前开始使用 OpenMP。我想知道如何使用#pragma使每个内核运行一个线程。详细信息:- int ncores = omp_get_num_procs();for(i = 0; i <
我有一段代码(它是应用程序的一部分),我正在尝试使用 OpenMP 对其进行优化,正在尝试各种调度策略。就我而言,我注意到 schedule(RUNTIME)条款比其他条款有优势(我没有指定 chun
我有一个数字运算 C/C++ 应用程序。它基本上是不同数据集的主循环。我们可以使用 openmp 和 mpi 访问一个 100 节点的集群。我想加速应用程序,但我是 mpi 和 openmp 的绝对新
在 OpenMP 中使用ompsections时,线程会被分配到sections内的 block ,还是每个线程会被分配到每个section? 当nthreads == 3时: #pragma omp
我正在尝试在 cython 中使用 openmp。我需要在 cython 中做两件事: i) 在我的 cython 代码中使用 #pragma omp single{} 作用域。 ii) 使用#pra
我正在尝试通过将循环的每次迭代作为 OpenMP 部分来并行化 OpenMP 中基于范围的 for 循环。我想这样做: #pragma omp parallel sections { for ( au
我正在尝试在 cython 中使用 openmp。我需要在 cython 中做两件事: i) 在我的 cython 代码中使用 #pragma omp single{} 作用域。 ii) 使用#pra
我想编写一个代码转换器,它采用基于 OpenMP 的并行程序并在集群上运行它。 我该如何解决这个问题?我使用哪些库?如何为此设置小型集群? 我发现很难在 Internet 上找到有关集群计算的好 Ma
我是 OpenMP 的新手。我正在尝试为 for 循环使用多个内核,但出现此编译错误: “错误 C3016:'x':OpenMP 'for' 语句中的索引变量必须具有带符号的整数类型”。 我知道 Op
如果我使用 VS 2010 编译器从 Qt Creator 构建项目,我如何启用 OpenMP(从 Visual Studio 构建时,您只需启用该功能)谢谢 最佳答案 在 .pro 文件中尝试下一步
我是一名优秀的程序员,十分优秀!