C++ 使用多处理器加速程序

C++ 使用多处理器加速程序,c++,c,performance,parallel-processing,C++,C,Performance,Parallel Processing,我发现有时将一个循环分成两个或更多个循环会更快 for (i=0; i<AMT; i++) { a[i] += c[i]; b[i] += d[i]; } || \/ for (i=0; i<AMT; i++) { //a[i] += c[i]; b[i] += d[i]; } for (i=0; i<AMT; i++) { a[i] += c[i]; //b[i] += d[i]; } for(i=0;i优

我发现有时将一个循环分成两个或更多个循环会更快

for (i=0; i<AMT; i++) {
    a[i] += c[i];
    b[i] += d[i];
}
     ||
     \/
for (i=0; i<AMT; i++) {
    //a[i] += c[i];
    b[i] += d[i];
}
for (i=0; i<AMT; i++) {
    a[i] += c[i];
    //b[i] += d[i];
}

for(i=0;i优化由编译器()完成。
如果您使用的是GCC,请查看此页面以获取可用优化规则的列表

另一方面,请注意您正在使用的rand()函数会占用大量CPU时间

我想问一下我的猜测是否正确,如果我是对的,有哪些规则或场合会自动使用多个处理器(无需线程编程)来加速我的程序

不,猜测是不对的。在这三种情况下,代码都在一个内核上运行


由于其他原因,将第一个循环拆分为两个会加快速度。也许编译器可以生成更好的代码,或者CPU可以更轻松地预取正确的数据,等等。如果不分析生成的机器代码,很难判断编译器是否是更简单的循环。在assemb中ler输出您将看到这是使用SIMD指令编译的程序(如)一次处理大于一个数字的大块数据。自动矢量化是一个困难的问题,编译器可能无法对同时更新
a
b
的循环进行矢量化。这可以部分解释为什么将复杂循环分成两个会更快

在“赋值”循环中,对
rand()的每次调用
取决于之前调用的输出,这意味着向量化本质上是不可能的。将循环一分为二并不会像第一种情况那样从SIMD指令中获益,因此您不会看到它运行得更快。查看编译器生成的汇编代码可以告诉您com的优化比勒执行了哪些操作以及使用了哪些指令

即使编译器对循环进行矢量化,程序也不会使用多个CPU或线程;没有并发。发生的情况是,有一个CPU能够在多个数据点上并行运行单个执行线程。并行编程和并发编程之间的区别很微妙,但很重要


缓存局部性还可以解释为什么将第一个循环一分为二会使它运行得更快,但却不能解释为什么将“分配”循环一分为二不会使它运行得更快。在“分配”中可能存在
b
c
循环足够小,因此它们适合缓存,这意味着循环已经具有最佳性能,进一步破坏它不会带来任何好处。如果是这种情况,将
b
c
变大将迫使循环开始破坏缓存,将循环分成两部分将获得预期的好处t、

这是一个关于CPU缓存的问题。这里有一篇关于CPU缓存的文章,我认为在多核上运行单线程应用程序是不可能的。然而,这里有一个链接挑战了我的信念……感谢这些链接,我正在阅读。缓存注释仍然适用于第二个示例,但我猜
rand()
的速度足够慢,这无关紧要吗?请注意,本问题中的示例和性能数字与链接副本的匹配非常紧密。因此,虽然我不能100%确定这是同一原因,但它们似乎指向同一方向。因此,这不是因为自动并行。这是关于对齐和hav正在拆分太多的访问流。第二种情况下看不到差异的原因是
rand()
是一个昂贵的操作,它掩盖了最初的问题。我同意拆分循环是一种优化(循环裂变,)但是你确定编译器正在进行优化吗?OP似乎通过手工进行优化而受益(至少在问题的第一个示例中)…OP可以像用户示例一样手工完成。编译器可以完成(至少GCC支持它)。有关GCC优化选项的更多详细信息,请从低级角度参考本页,Mystical已在类似问题中对此进行了详细解释(抱歉重复).虽然我目前无法理解缓存和别名的内容。贝希尔的回答和马蒂亚的评论确实从更高的层次解释了事情,这是一个循环的裂变和融合。然而,要理解为什么裂变在我的情况下会加速,我仍然需要在另一篇文章中学习神秘主义的答案。好吧,谢谢你们,我有一个答案现在有很多页要读:-)神秘主义告诉你的主要事情是第一种情况下的“循环裂变中引用的位置”(from),第二种情况下的“rand()是一个昂贵的操作,它掩盖了最初的问题”。是的,我一直在试图了解为什么循环裂变可以改善引用的位置。。。很幸运我来到了这个地方,现在我知道要成为一名优秀的程序员,我还需要知道更多的东西。我倾向于同意(尽管我在这里有点力不从心)。但奇怪的是,在这篇文章()中(这似乎是OP手工完成的技术),有一句话说“[这种]优化在多核处理器中最有效,可以为每个处理器将一个任务拆分为多个任务”。你是说猜测并不总是正确的,还是仅仅针对OP提到的特定处理器?那篇文章的这一点是错误的。循环裂变可以作为将两个循环拆分为单独线程的先驱,但我所见过的任何多核处理器都无法单独检测到这一点。我不确定我是否遵循“仅复制一块内存”位。不,根本没有复制。SIMD指令的使用(或不使用)与此无关。与传统的浮点指令相比,现在大多数编译器更喜欢SIMD指令。“英特尔编译器”是为数不多的能够对这样的代码进行矢量化从而最大限度地使用SIMD的编译器之一
for (i=0; i<AMT; i++) {
    b[i] = rand()%100;
    c[i] = rand()%100;
}