老哥们,请教一下下面的程序是因为什么原因造成的?
#include <iostream>
#include <chrono>
using namespace std;
class Runtime{
private:
std::chrono::high_resolution_clock::time_point _start, _end;
public:
Runtime(){
start();
}
void start(){
_start = std::chrono::high_resolution_clock::now();
}
void end() {
_end = std::chrono::high_resolution_clock::now();
}
long long spend() {
return std::chrono::duration_cast<std::chrono::nanoseconds>(_end - _start).count();
}
void printTime() {
end();
std::cout << "time = "
<< spend()/1000000 << "/ms = "//毫秒
<< spend()/1000 << "/us = "//微秒
<< spend() << "/ns" << std::endl;//纳秒
}
};
const int N = 1e5;
int x[N];
int y[N];
int z[N];
//inline
int calc(int a, int b) {
return a + b;
}
int main(){
Runtime r;
for (int i=0; i<N; ++i) {
for (int j=0; j<N; ++j) {
for (int k=0; k<N; ++k)
z[i] = calc(y[j], x[k]);
//z[i] = y[j] + x[k];
}
}
r.printTime();
}
加了 inline 比不加慢一倍,不是说 inline 会减少函数压栈出栈的时间开销,相当于直接在调用点插入代码嘛,但是,实际上开销比不加 inline 慢一倍,不加 inline 的开销和下面注释的那句的开销差不多。感觉很奇怪,难道是编译器自动添加了 inline ?
请问是什么原因造成的啊?
1
BrettD 2020-12-19 18:41:37 +08:00 via iPhone
看一下生成的汇编代码是啥
|
2
Huelse 2020-12-19 18:50:42 +08:00
据我所知,一些编译器是会在编译 release 或者有其他编译选项的时候会自动给`return a+b`这种自动加 inline
|
3
crclz 2020-12-19 18:54:49 +08:00
1. 看看汇编,用 vscode 比较一下(右键)
2. 开-O1 或者-O2,再测一下速度,并且再比较一下汇编代码 |
4
YouLMAO 2020-12-19 18:55:17 +08:00
for (int i=0; i<N; ++i) {
z[i] = y[N] + x[N]; } 这就是 gcc 优化的结果 |
5
GeruzoniAnsasu 2020-12-19 19:04:44 +08:00
首先开不开优化影响很大,如果被 inline 的代码本身 side effect 比较多,未优化情况下可能会额外多做很多事情,比如频繁读写变量的内存,本来这都是完全不需要的
另外现在 x64 的 fastcall abi,函数调用已经没有访问内存出入栈的开销了,都可以用寄存器解决,所以 inline 省略掉了函数出入栈这个观念也已经是过时甚至错误的了 再然后,众所周知 c++编译器行为学都只能马后炮,自己看一眼编译出来的汇编就都明白了 |
6
zvl0reqglvd OP 要的,谢谢老哥们,鞠躬!!
|
7
lifanxi 2020-12-19 19:10:35 +08:00
看看汇编找原因。
具体是什么环境出现了你说描述的现象?我在 gcc 8.3 上重现不出来你说的现象。 |
8
nightwitch 2020-12-19 19:16:36 +08:00 3
inline 这个关键词建议直接忘记它有内联建议这个功能,因为现在编译器基本上无视 inline 的这个语义。
如果你真的确定要内联一个函数,MSVC 要用__forceinline , gcc 要用__attribute__((always_inline))。 inline 这个关键词现在的主要作用是允许一个符号在不同的编译单元有重复的定义,这样允许你在头文件里写出函数实现和 inline variable. |
9
mxalbert1996 2020-12-19 19:18:03 +08:00 via Android
不知道你这个是不是这种情况,不过 inline 可能会造成更多 cache miss 从而影响性能。
https://en.wikipedia.org/wiki/Inline_expansion Inlining also imposes a cost on performance, due to the code expansion (due to duplication) hurting instruction cache performance.[6] This is most significant if, prior to expansion, the working set of the program (or a hot section of code) fit in one level of the memory hierarchy (e.g., L1 cache), but after expansion it no longer fits, resulting in frequent cache misses at that level. Due to the significant difference in performance at different levels of the hierarchy, this hurts performance considerably. |
10
msg7086 2020-12-19 19:44:25 +08:00
gcc 带上优化编译出来是这样的。
main: vpbroadcastd ymm1, DWORD PTR x[rip+399996] mov eax, OFFSET FLAT:z mov ecx, OFFSET FLAT:y+400000 .L3: mov edx, OFFSET FLAT:y .L2: vpbroadcastd ymm0, DWORD PTR [rdx] add rdx, 4 cmp rcx, rdx jne .L2 vpaddd ymm0, ymm1, ymm0 add rax, 32 vmovdqa YMMWORD PTR [rax-32], ymm0 cmp rax, OFFSET FLAT:z+400000 jne .L3 xor eax, eax vzeroupper ret z: .zero 400000 y: .zero 400000 x: .zero 400000 打开 AVX2 以后会直接执行 VPADDD,不仅不会调用 calc,甚至不会调用普通的寄存器加法。 |
11
codehz 2020-12-20 00:22:04 +08:00 via Android
(inline 有你认为的效果要么是编译器版本太旧,要么就是你用了 always_inline 等私有扩展。。。
按现行 c++标准,inline 关键字不再作为内联使用了) 至于你这里的问题,你可能没开优化测试的吧 |
12
786375312123 2020-12-20 00:30:45 +08:00
看看你的 vs 优化开没开
|
13
no1xsyzy 2020-12-20 03:04:12 +08:00
@mxalbert1996 按楼主代码,没有反复写 calc(),发生内联相比不发生内联,也不过是把唯一一处 calc(a, b) 替换为 a+b,并且还去掉了 calc 本身的定义,可以说指令的内存占用反而是减少的。
|
14
codyfeng 2020-12-20 09:09:23 +08:00 via Android
这类问题应该把编译器版本、参数都贴出来
|
15
mxT52CRuqR6o5 2020-12-20 14:39:25 +08:00
https://godbolt.org/
不开编译优化我这边看这个在线的编译器结果 gcc 是完全一样,clang 是编译出来的顺序不一样,都没有 inline 想要弄清区别肯定是要看汇编的,不知道你用的什么编译器开的什么编译选项 |
16
zvl0reqglvd OP @mxT52CRuqR6o5 老哥你好,我用的是 gcc9.2,然后运行的是 g++ -g test.cpp -o test -lm -Wall -std=c++17 -O2 && test.exe ,开了优化的。
|
17
Gwkang 2020-12-20 20:03:51 +08:00 via Android
频繁调用的函数,不内联可以提高 CPU 缓存命中率,你内联之后就没有了函数调用栈,所以慢
|