我有一个带有多个 for 循环的 C++ 程序;每个运行大约 500 万次迭代。是否有任何命令可以与 g++ 一起使用来使用多核生成生成的 .exe? IE。使第一个 for 循环在第一个核心上运行,第二个 for 循环在第二个核心上同时运行?我尝试过
-O3
和-O3 -ftree-vectorize
,但在这两种情况下,我的CPU使用率仍然只徘徊在25%左右。
这是我的代码,以防有帮助。我基本上只是在编写一个程序来测试我的计算机的速度能力。
#include <iostream>
using namespace std;
#include <math.h>
int main()
{
float *bob = new float[50102133];
float *jim = new float[50102133];
float *joe = new float[50102133];
int i,j,k,l;
//cout << "Starting test...";
for (i=0;i<50102133;i++)
bob[i] = sin(i);
for (j=0;j<50102133;j++)
bob[j] = sin(j*j);
for (k=0;k<50102133;k++)
bob[k] = sin(sqrt(k));
for (l=0;l<50102133;l++)
bob[l] = cos(l*l);
cout << "finished test.";
cout << "the 100120 element is," << bob[1001200];
return 0;
}
最明显的选择是使用 OpenMP。假设您的循环非常容易并行执行多个迭代,您可能只需添加:
#pragma openmp parallel for
...紧接在循环之前,并使其并行执行。编译时还必须添加
-fopenmp
。
根据循环的内容,这可能会带来从近乎线性的加速到稍微减慢代码的速度。在后一种情况下(速度减慢或加速最小),您可能可以使用 OpenMP 执行其他操作来帮助加速,但如果不了解至少一点代码本身,就很难猜测要做什么或可以进行哪些改进能够最大程度地期待。
您得到的其他建议(“使用线程”)可能适合。 OpenMP 基本上是一种将线程用于特定类型并行代码的自动化方法。对于您所描述的情况(并行执行循环的多个迭代),OpenMP 通常是首选 - 它实现起来更简单,并且很可能提供更好的性能,除非您非常了解多线程和/或花费大量精力并行化代码的努力。
编辑:
您在问题中给出的代码可能不会从多线程中受益。问题在于,在将结果写入内存之前,它对每个数据项执行的计算非常少。即使是单个核心也可能足够快地进行计算,以至于整体速度将受到内存带宽的限制。
为了有很大机会从多线程中获得一些真正的好处,您可能需要编写一些执行更多计算而不只是读写内存的代码。例如,如果我们将您的计算折叠在一起,并在单个项目上执行所有计算,然后对结果求和:
double total = 0;
for (int i = 0; i < size; i++)
total += sin(i) + sin(i*i) + sin(sqrt(i)) + cos(i*i);
通过添加编译指示:
#pragma omp parallel for reduction(+:total)
...就在
for
循环之前,我们很有可能看到执行速度的显着提高。如果没有 OpenMP,我会遇到这样的情况:
Real 16.0399
User 15.9589
Sys 0.0156001
...但是在编译时启用了
#pragma
和 OpenMP,我得到这样的时间:
Real 8.96051
User 17.5033
Sys 0.0468003
因此,在我的(双核)处理器上,时间从 16 秒下降到 9 秒——虽然速度不是两倍,但也相当接近了。当然,您获得的很多改进将取决于您有多少个可用核心。例如,在我的另一台计算机(配备 Intel i7 CPU)上,我得到了相当大的改进,因为它有更多的内核。
没有 OpenMP:
Real 15.339
User 15.3281
Sys 0.015625
...以及 OpenMP:
Real 3.09105
User 23.7813
Sys 0.171875
为了完整起见,这是我使用的最终代码:
#include <math.h>
#include <iostream>
static const int size = 1024 * 1024 * 128;
int main(){
double total = 0;
#pragma omp parallel for reduction(+:total)
for (int i = 0; i < size; i++)
total += sin(i) + sin(i*i) + sin(sqrt(i)) + cos(i*i);
std::cout << total << "\n";
}
编译器无法判断循环内的代码是否可以在多个内核上安全执行。如果您想使用所有核心,请使用线程。
使用线程或进程,您可能需要查看OpenMp
C++11 支持线程,但 C++ 编译器不会/无法自行执行任何线程。
正如其他人所指出的,您可以手动使用线程来实现这一点。您可以查看诸如 libdispatch(又名 GCD)或 Intel 的 TBB 之类的库来帮助您以最少的痛苦完成此任务。
您提到的-ftree-vectorize
选项用于针对CPU上的SIMD矢量处理器单元,例如ARM的NEON或Intel的SSE。生成的代码不是线程并行的,而是使用单个线程并行操作。 上面发布的代码示例非常适合 SIMD 系统上的并行性,因为每个循环的主体显然不依赖于前一个迭代,并且循环中的操作是线性的。
至少在某些 ARM Cortex A 系列系统上,您可能需要接受稍微降低的精度才能获得全部优势。