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