gcc能使我的代码并行吗

Can gcc make my code parallel?

本文关键字:并行 代码 我的 gcc      更新时间:2023-10-16

我想知道gcc中是否有一个优化,可以使一些单线程代码(如下面的示例(并行执行。如果没有,为什么?如果是,可以进行哪种优化?

#include <iostream>
int main(int argc, char *argv[])
{
    int array[10];
    for(int i = 0; i < 10; ++ i){
        array[i] = 0;
    }
    for(int i = 0; i < 10; ++ i){
        array[i] += 2;
    }
    return 0;
}

添加:

感谢您提供OpenMP链接,尽管我认为它很有用,但我的问题与编译相同的代码有关,而无需重写smth。所以基本上我想知道是否:

  1. 在不重写的情况下使代码并行(至少在某些情况下(是可能的吗
  2. 如果是,可以处理哪些案件?如果没有,为什么

编译器可以尝试自动并行代码,但不会通过创建线程来实现。它可以使用矢量化指令(例如,英特尔CPU的英特尔内部指令(一次对多个元素进行操作,在这种情况下,它可以检测到使用这些指令是可能的(例如,当您对正确对齐的数据结构的连续元素执行多次同一操作时(。您可以通过告诉编译器您的CPU支持哪个内部指令集(例如-mavx, -msse4.2 ...(来帮助编译器。

您也可以直接使用这些指令,但这需要程序员做大量的工作。还有一些库已经做到了这一点(请参阅AgnerFog博客中的向量类(。

通过使用OpenMP(OpenMP Introduction(,可以让编译器使用多个线程进行自动并行,这比编译器自己进行自动并行更能指导编译器进行自动并行。

是的,例如,带有-ftree-parallelize-loops=4的gcc将尝试使用4个线程自动并行。

我不知道gcc在自动并行化方面做得有多好,但这是编译器开发人员多年来一直在做的事情。正如其他答案所指出的,用OpenMP杂注为编译器提供一些指导可以获得更好的结果。(例如,让编译器知道,事情发生的顺序无关紧要,即使这可能会稍微改变结果,这在浮点运算中很常见。浮点运算是不关联的。(

而且,只有对#pragma omp循环进行自动并行化意味着只有真正重要的循环才能得到这种处理。-ftree-parallelize-loops可能受益于PGO(概要文件引导优化(,以了解哪些循环实际上是热门的并且值得并行化和/或矢量化。

这在某种程度上与寻找SIMD可以利用的并行性有关,用于自动向量化循环。(默认情况下,它在gcc中的-O3和clang中的-O2处启用(。

只要可观察的行为(请参见1.9[interro.execution]第8段(与[correct(*(]程序指定的行为相同,编译器就可以为所欲为。可观察行为是根据I/O操作(使用标准C++库I/O(和对volatile对象的访问来指定的(尽管如果编译器能够证明volatile对象不在可观察内存中,则实际上并不需要对其进行特殊处理(。为此,C++执行系统可以采用并行技术。

您的示例程序实际上没有可观察的结果,编译器是一个很好的常量折叠程序,以发现程序实际上什么都不做。在最好的情况下,CPU辐射的热量可能是功的指示,但消耗的能量并不是可观察到的影响之一,也就是说,C++执行系统不需要这样做。如果您在启用优化(-O2或更高版本(的情况下使用clang编译上面的代码,它实际上会完全删除循环(使用-S选项让编译器发出汇编代码,以相当容易地检查结果(。

假设您实际上有被迫执行的循环,大多数当代编译器(至少是gcc、clang和icc(将尝试利用SIMD指令对代码进行矢量化。为此,编译器需要理解代码中的操作,以证明并行执行不会改变结果或引入数据竞赛(据我所知,当涉及浮点运算时,确切的结果实际上是,而不是,因为一些编译器很乐意并行化,例如,循环添加floats,尽管浮点添加不是关联的(。

我不知道有哪个当代编译器会利用不同的执行线程来提高执行速度,而不需要像Open MP的pragmas这样的提示。然而,委员会会议上的讨论意味着编译器供应商至少正在考虑这样做。

(*(在程序执行导致未定义行为的情况下,C++标准对C++执行系统没有任何限制。正确的程序不会调用任何形式的未定义行为。

tl;dr:允许但不要求编译器并行执行代码,大多数当代编译器在某些情况下都这样做。

如果你想并行化你的c++代码,你可以使用openmp。官方文档可以在这里找到:openmp-doc

Openmp提供了杂注,这样您就可以向编译器指示代码的一部分必须使用一定数量的线程。有时您可以手动执行,而其他一些杂注程序可以自动优化所使用的内核数量。

下面的代码是官方文档的一个示例:

#include <cmath>
int main() {
    const int size = 256;
    double sinTable[size];
    #pragma omp parallel for
    for(int n=0; n<size; ++n) {
        sinTable[n] = std::sin(2 * M_PI * n / size);
    }
}

此代码将自动并行化for循环,这将回答您的问题。openmp提供了许多其他可能性,您可以阅读文档了解更多信息。

如果您需要了解编译以支持openmp,请参阅以下堆栈溢出线程:openmp编译线程。

小心,如果不使用openmp特定的选项,则会忽略杂注,并且代码将在1个线程上运行。

我希望这能有所帮助。