我在我的机器上测试了下面的代码,看看我能获得多少吞吐量。除了为每个线程分配两个嵌套循环之外,代码没有做太多的工作,
#include <chrono>
#include <iostream>
int main() {
auto start_time = std::chrono::high_resolution_clock::now();
#pragma omp parallel for
for(int thread = 0; thread < 24; thread++) {
float i = 0.0f;
while(i < 100000.0f) {
float j = 0.0f;
while (j < 100000.0f) {
j = j + 1.0f;
}
i = i + 1.0f;
}
}
auto end_time = std::chrono::high_resolution_clock::now();
auto time = end_time - start_time;
std::cout << time / std::chrono::milliseconds(1) << std::endl;
return 0;
}令我惊讶的是,根据perf,吞吐量非常低。
$ perf stat -e all_dc_accesses -e fp_ret_sse_avx_ops.all cmake-build-release/roofline_prediction
8907
Performance counter stats for 'cmake-build-release/roofline_prediction':
325.372.690 all_dc_accesses
240.002.400.000 fp_ret_sse_avx_ops.all
8,909514307 seconds time elapsed
202,819795000 seconds user
0,059613000 seconds sys在8.83秒内以240.002.400.000次失败,该机器仅获得27.1个GFLOPs/秒,远远低于CPU的392个GFLOPs/秒的容量(我从一个roofline建模软件中获得了这个数字)。
我的问题是,怎样才能达到更高的吞吐量?
1920X
发布于 2021-10-24 17:15:37
内部循环是用GCC 9.3编写的,带有这些选项,如下所示:
.L3:
addss xmm0, xmm2
comiss xmm1, xmm0
ja .L3GCC版本/选项的其他一些组合可能会导致循环被删除,毕竟它并没有真正做任何事情(除了浪费时间)。
addss形成一个循环携带的依赖关系,其中只包含它自己。但这并不快,在每次迭代3次的Zen 1上,每个周期的加法次数为1/3。每个周期可以通过至少有6个独立的addps指令(256位vaddps可能会有所帮助,但Zen 1执行这种256位SIMD指令,内部有2个128位操作)来处理3的延迟和每周期2次的吞吐量(因此在任何时候都需要激活6个操作)。这相当于每周期增加8次,是当前代码的24倍。
从C++程序中,可以通过以下方法引导编译器生成合适的机器代码:
使用_mm_add_ps
-ffast-math (如果可能的话,它并不总是这样)的展开循环
https://stackoverflow.com/questions/69698932
复制相似问题