【问题标题】:Make g++ produce a program that can use multiple cores?让g++生成一个可以使用多核的程序?
【发布时间】:2014-04-02 18:46:32
【问题描述】:

我有一个带有多个 For 循环的 c++ 程序;每个都运行大约 500 万次迭代。是否有任何命令可以与 g++ 一起使用以使生成的 .exe 使用多个内核;即让第一个 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;
}

【问题讨论】:

标签: c++ gcc optimization g++ core


【解决方案1】:

C++11 支持 threading,但 c++ 编译器不会/不能自行执行任何线程。

【讨论】:

    【解决方案2】:

    最明显的选择是使用 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";
    }
    

    【讨论】:

    • 谢谢!我尝试了您的优化代码,通过优化我能够在 30 秒内运行约 50 亿次计算(而没有 -fopenmp 的情况下几乎需要 2 分钟),而原始的内存密集型程序每秒迭代约 500 万次。
    【解决方案3】:

    编译器无法判断循环内的代码是否可以在多个内核上安全执行。如果您想使用所有内核,请使用线程。

    【讨论】:

      【解决方案4】:

      使用线程或进程,你可能想看看OpenMp

      【讨论】:

        【解决方案5】:

        正如其他人所指出的,您可以手动使用线程来实现这一点。您可能会查看诸如 libdispatch(又名 GCD)或 Intel's TBB 之类的库,以帮助您以最少的痛苦做到这一点。

        您提到的-ftree-vectorize 选项用于针对CPU 上的SIMD 矢量处理器单元,例如ARM 的NEON 或Intel 的SSE。生成的代码不是线程并行的,而是使用单个线程并行操作。

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

        至少在某些 ARM Cortex A 系列系统上,您可能需要接受略微降低的精度才能获得全部优势。

        【讨论】:

          猜你喜欢
          • 2011-11-06
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2018-08-09
          相关资源
          最近更新 更多