让g++产生一个可以使用多核心的程序

Make g++ produce a program that can use multiple cores?

本文关键字:可以使 多核 核心 程序 一个 g++      更新时间:2023-10-16

我有一个带有多个For循环的c++程序;每一个运行大约500万次迭代。有没有任何命令我可以与g++一起使用,使生成的.exe使用多个内核;即使第一个For循环同时在第一个核心上运行,使第二个For循环在第二个核心上同时运行?我尝试过-O3和-O3-ftree矢量化,但在这两种情况下,我的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秒——速度不是原来的两倍,但非常接近。当然,你得到的很多改进将取决于你有多少核心可用。例如,在我的另一台电脑(使用英特尔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选项是针对ARM的NEON或Intel的SSE等CPU上的SIMD矢量处理器单元。生成的代码不是线程并行的,而是使用单个线程的并行操作。

上面发布的代码示例非常适合SIMD系统上的并行性,因为每个循环的主体显然不依赖于上一次迭代,并且循环中的操作是线性的。

至少在一些ARM Cortex A系列系统上,您可能需要接受稍微降低的精度才能获得全部好处。