C++ 编译器在这里执行哪个循环优化?

C++ 编译器在这里执行哪个循环优化?,c++,multithreading,for-loop,optimization,C++,Multithreading,For Loop,Optimization,假设以下代码: #include <thread> #include <iostream> #include <vector> #include <future> #include <chrono> #include <cmath> long long int partialSum(const std::vector<int>& v, int begin, int end) { long lon

假设以下代码:

#include <thread>
#include <iostream>
#include <vector>
#include <future>
#include <chrono>
#include <cmath>

long long int partialSum(const std::vector<int>& v, int begin, int end) {
    long long int sum = 0;
    for (int i = begin; i < end; i++) {
        sum += (v[i]);
    }
    return sum;
}

int main() {
    std::vector<int> v(10000000, 1);

    //2 threads
    auto start = std::chrono::high_resolution_clock::now();
    std::future<long long int> f1 = std::async(std::launch::async, partialSum, v, 0, 10000000 /2);
    std::future<long long int> f2 = std::async(std::launch::async, partialSum, v, 10000000 / 2, 10000000);
    volatile long long int a = f1.get() + f2.get();
    auto end = std::chrono::high_resolution_clock::now();
    auto duration = std::chrono:: duration_cast<std::chrono::microseconds>(end - start);
    std::cout << "With 2 threads-> " << duration.count() << std::endl;

    //1 thread
    start = std::chrono::high_resolution_clock::now();
    volatile long long int b = partialSum(v, 0, 10000000);
    end = std::chrono::high_resolution_clock::now();
    duration = std::chrono::duration_cast<std::chrono::microseconds>(end - start);
    std::cout << "With 1 thread-> " << duration.count() << std::endl;
}
注意,我必须添加
volatile
,以避免编译器执行更多优化。 还请注意,我知道在
std
中有一个
accumulate
,但我目前正在学习多线程,这是一个POC。 基本上,我想知道编译器在这里进行哪种优化,因为与线程版本相比,它的优化效果非常好。 当我更改
partialSum
并替换操作(可能
log10
)时,线程版本是常规版本的两倍

编辑: 在提出一些建议后,我将代码更改为以下代码:

#include <thread>
#include <iostream>
#include <vector>
#include <future>
#include <chrono>
#include <cmath>

long long int partialSum(const std::vector<int>& v, int begin, int end) {
    long long int sum = 0;
    for (int i = begin; i < end; i++) {
        sum += (v[i]);
    }
    return sum;
}

int main() {
    std::vector<int> v(10000000, 1);

    //2 threads
    auto start = std::chrono::high_resolution_clock::now();
    std::future<long long int> f1 = std::async(std::launch::async, partialSum, std::cref(v), 0, 10000000 /2);
    std::future<long long int> f2 = std::async(std::launch::async, partialSum, std::cref(v), 10000000 / 2, 10000000);
    volatile long long int a = f1.get() + f2.get();
    auto end = std::chrono::high_resolution_clock::now();
    auto duration = std::chrono:: duration_cast<std::chrono::microseconds>(end - start);
    std::cout << "With 2 threads-> " << duration.count() << std::endl;

    //1 thread
    start = std::chrono::high_resolution_clock::now();
    f1 = std::async(std::launch::async, partialSum, std::cref(v), 0, 10000000);
    end = std::chrono::high_resolution_clock::now();
    duration = std::chrono::duration_cast<std::chrono::milliseconds>(end - start);
    std::cout << "With 1 thread-> " << duration.count() << std::endl;
}

之所以存在性能差距,是因为您实际上是在复制向量:

std::future<long long int> f1 = std::async(std::launch::async, partialSum, v, 0, 10000000 /2);
std::future<long long int> f2 = std::async(std::launch::async, partialSum, v, 10000000 / 2, 10000000);
然后再次尝试测量性能。对我来说,更改后的2线程版本更快。请在此处尝试:

您的第二个代码段正在为1个线程打印
0
,因为您没有等待
f1
完成

在获取
结束
值之前,请执行此操作:

volatile long long int b = f1.get();

至于循环优化(这一部分对于OP可能是不必要的),编译器(GCC)正在对循环进行矢量化(没有任何
-march=
选项)。生成的asm如下所示:

.L399:
        movdqu  xmm0, XMMWORD PTR [rax]
        movdqa  xmm2, xmm4
        add     rax, 16
        pcmpgtd xmm2, xmm0
        movdqa  xmm3, xmm0
        punpckldq       xmm3, xmm2
        punpckhdq       xmm0, xmm2
        paddq   xmm1, xmm3
        paddq   xmm1, xmm0
        cmp     rdx, rax
        jne     .L399
如果我们使用
int
而不是
long-long-int
,编译器就可以更容易地进一步优化它。然后,asm输出减小为:

        pxor    xmm0, xmm0
.L13:
        movdqu  xmm2, XMMWORD PTR [rdx]
        add     rdx, 16
        paddd   xmm0, xmm2
        cmp     rcx, rdx
        jne     .L13

切换顺序(先1个线程,后2个线程)。。。你得到相同或相似的结果吗?@ChrisMM是的,非常相似可能有两件事:函数实际上相当快,开销来自线程启动(我不知道需要多长时间,这也取决于你的操作系统);由于log10示例可能需要更长的时间,因此开销不再重要。另一件事可能是线程以某种方式相互干扰缓存线(我不知道这是否可能);但对于在内存上运行的廉价函数来说,缓存是王道。@lalala我仍然认为这里可能有一些幕后的东西。这可能是线程创建/控制带来的开销,但我认为这与缓存无关。@G.Sliepen如果
sum
I
实际上在内存中,编译器应该是firedI,请看,性能会这样提高,但是使用你的链接,我得到了2个线程->5834和1个线程->5333仍然有线程创建开销,对我来说,它是
和2个线程->5956和1个线程->6140
,有时
和线程1
是快速的请看我的编辑,我还为1个线程版本创建了另一个线程,使用异步,检查结果,看来你是对的。f1.get()等待结果,不需要等待(),但问题是我没有为1线程版本执行get()。如果你想把这个加在你的答案上,我会接受的。
volatile long long int b = f1.get();
.L399:
        movdqu  xmm0, XMMWORD PTR [rax]
        movdqa  xmm2, xmm4
        add     rax, 16
        pcmpgtd xmm2, xmm0
        movdqa  xmm3, xmm0
        punpckldq       xmm3, xmm2
        punpckhdq       xmm0, xmm2
        paddq   xmm1, xmm3
        paddq   xmm1, xmm0
        cmp     rdx, rax
        jne     .L399
        pxor    xmm0, xmm0
.L13:
        movdqu  xmm2, XMMWORD PTR [rdx]
        add     rdx, 16
        paddd   xmm0, xmm2
        cmp     rcx, rdx
        jne     .L13