gpt4 book ai didi

multithreading - openmp 并行 for 循环的性能出乎意料的好

转载 作者:行者123 更新时间:2023-12-04 01:05:48 39 4
gpt4 key购买 nike

我在之前的评论(尤其是@Zboson)之后编辑了我的问题,以获得更好的可读性

我一直遵循并观察到 ​​Openmp 线程的数量应该与机器上的超线程数量大致匹配以获得最佳性能的传统观点。但是,我在配备 Intel Core i7 4960HQ、4 核 - 8 线程的新笔记本电脑上观察到异常行为。 (见 Intel docs here)

这是我的测试代码:

#include <math.h>
#include <stdlib.h>
#include <stdio.h>
#include <omp.h>

int main() {
const int n = 256*8192*100;
double *A, *B;
posix_memalign((void**)&A, 64, n*sizeof(double));
posix_memalign((void**)&B, 64, n*sizeof(double));
for (int i = 0; i < n; ++i) {
A[i] = 0.1;
B[i] = 0.0;
}
double start = omp_get_wtime();
#pragma omp parallel for
for (int i = 0; i < n; ++i) {
B[i] = exp(A[i]) + sin(B[i]);
}
double end = omp_get_wtime();
double sum = 0.0;
for (int i = 0; i < n; ++i) {
sum += B[i];
}
printf("%g %g\n", end - start, sum);
return 0;
}

当我使用 gcc 4.9-4.9-20140209 编译它时,使用命令: gcc -Ofast -march=native -std=c99 -fopenmp -Wa,-q当我更改 OMP_NUM_THREADS 时,我看到了以下性能[这些点是 5 次运行的平均值,误差线(几乎看不到)是标准偏差]:
Performance as a function of thread count

当显示为相对于 OMP_NUM_THREADS=1 的加速时,该图更清晰:
Speed up as a function of thread count

性能或多或少随着线程数单调增加,即使 omp 线程数大大超过核心和超线程数!通常,当使用过多线程时(至少在我以前的经验中),由于线程开销,性能应该会下降。特别是因为计算应该是cpu(或至少是内存)绑定(bind)并且不等待I/O。

更诡异的是,加速是35倍!

谁能解释一下?

我还用小得多的阵列 8192*4 对此进行了测试,并看到了类似的性能扩展。

万一这很重要,我在 Mac OS 10.9 和通过运行(在 bash 下)获得的性能数据:
for i in {1..128}; do
for k in {1..5}; do
export OMP_NUM_THREADS=$i;
echo -ne $i $k "";
./a.out;
done;
done > out

编辑:出于好奇,我决定尝试更多数量的线程。我的操作系统将此限制为 2000。奇怪的结果(加速和低线程开销)不言自明!
Crazy numbers of threads

编辑:我在他们的回答中尝试了@Zboson 的最新建议,即将 VZEROUPPER 放在循环中的每个数学函数之前,它确实解决了缩放问题! (它还将单线程代码从 22 秒发送到 2 秒!):

correct scaling

最佳答案

问题可能是由于 clock()功能。它不会返回 Linux 上的挂钟时间。你应该使用函数omp_get_wtime() .它比时钟更准确,适用于 GCC、ICC 和 MSVC。事实上,即使我不使用 OpenMP,我也将它用于计时代码。

我在这里用它测试了你的代码
http://coliru.stacked-crooked.com/a/26f4e8c9fdae5cc2

编辑 :要考虑的另一件事可能导致您的问题是 expsin您正在使用的函数是在没有 AVX 支持的情况下编译的。您的代码是使用 AVX 支持编译的(实际上是 AVX2)。你可以从 GCC explorer 看到这个如果您使用 -fopenmp -mavx2 -mfma 编译,请使用您的代码每当您从带有 AVX 的代码中调用不支持 AVX 的函数时,您需要将 YMM 寄存器的上部归零或支付大笔罚款。你可以用内在的 _mm256_zeroupper 来做到这一点。 (VZEROUPPER)。 Clang 为您执行此操作,但最后我检查了 GCC 没有,因此您必须自己执行(请参阅此问题的评论 Math functions takes more cycles after running any intel AVX function 以及此处的答案 Using AVX CPU instructions: Poor performance without "/arch:AVX")。因此,由于没有调用 VZEROUPPER,每次迭代都会有很大的延迟。我不确定为什么这对多线程很重要,但如果 GCC 每次启动一个新线程时都这样做,那么它可以帮助解释你所看到的。

#include <immintrin.h>

#pragma omp parallel for
for (int i = 0; i < n; ++i) {
_mm256_zeroupper();
B[i] = sin(B[i]);
_mm256_zeroupper();
B[i] += exp(A[i]);
}

编辑 一个更简单的测试方法是使用 -march=native 来代替编译。不要设置拱门 ( gcc -Ofast -std=c99 -fopenmp -Wa ) 或只使用 SSE2 ( gcc -Ofast -msse2 -std=c99 -fopenmp -Wa )。

编辑 GCC 4.8有一个选项 -mvzeroupper这可能是最方便的解决方案。

This option instructs GCC to emit a vzeroupper instruction before a transfer of control flow out of the function to minimize the AVX to SSE transition penalty as well as remove unnecessary zeroupper intrinsics.

关于multithreading - openmp 并行 for 循环的性能出乎意料的好,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/21960229/

39 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com