N******K 发帖数: 10202 | 1 编译器 vs2013 release模式
Tempfunction 计算一个数然后输出 N 控制计算复杂度
我用function pointer :TempfunctionPtr = &Tempfunction
然后比较 直接调用函数Tempfunction 和 通过指针间接调用函数
结果很诡异
N=1000 , Tempfunction_time=1, TempfunctionPtr_time=2;
N=2000 , Tempfunction_time=2, TempfunctionPtr_time=6;
N=4000 , Tempfunction_time=4, TempfunctionPtr_time=11;
N=8000 , Tempfunction_time=8, TempfunctionPtr_time=21;
TempfunctionPtr_time - Tempfunction_time 应该是常数
N并不改变 函数调用的次数
=================
#include
#include
void Tempfunction(double& a, int N)
{
a = 0;
for (double i = 0; i < N; ++i)
{
a += i;
}
}
int main()
{
int N = 1000; // from 1000 to 8000
double Value = 0;
auto t0 = std::time(0);
for (int i = 0; i < 1000000; ++i)
{
Tempfunction(Value, N);
}
auto t1 = std::time(0);
auto Tempfunction_time = t1-t0;
std::cout << "Tempfunction_time = " << Tempfunction_time << '\n';
auto TempfunctionPtr = &Tempfunction;
Value = 0;
t0 = std::time(0);
for (int i = 0; i < 1000000; ++i)
{
(*TempfunctionPtr)(Value, N);
}
t1 = std::time(0);
auto TempfunctionPtr_time = t1-t0;
std::cout << "TempfunctionPtr_time = " << TempfunctionPtr_time << '\n';
std::system("pause");
} | n*****t 发帖数: 22014 | 2 高手没空,蓝翔技校试着回答:
间接调用开销稍大,不过貌似不该差那么多
【在 N******K 的大作中提到】 : 编译器 vs2013 release模式 : Tempfunction 计算一个数然后输出 N 控制计算复杂度 : 我用function pointer :TempfunctionPtr = &Tempfunction : 然后比较 直接调用函数Tempfunction 和 通过指针间接调用函数 : 结果很诡异 : N=1000 , Tempfunction_time=1, TempfunctionPtr_time=2; : N=2000 , Tempfunction_time=2, TempfunctionPtr_time=6; : N=4000 , Tempfunction_time=4, TempfunctionPtr_time=11; : N=8000 , Tempfunction_time=8, TempfunctionPtr_time=21; : TempfunctionPtr_time - Tempfunction_time 应该是常数
| n*****t 发帖数: 22014 | 3 你可以 O0 试试看
【在 N******K 的大作中提到】 : 编译器 vs2013 release模式 : Tempfunction 计算一个数然后输出 N 控制计算复杂度 : 我用function pointer :TempfunctionPtr = &Tempfunction : 然后比较 直接调用函数Tempfunction 和 通过指针间接调用函数 : 结果很诡异 : N=1000 , Tempfunction_time=1, TempfunctionPtr_time=2; : N=2000 , Tempfunction_time=2, TempfunctionPtr_time=6; : N=4000 , Tempfunction_time=4, TempfunctionPtr_time=11; : N=8000 , Tempfunction_time=8, TempfunctionPtr_time=21; : TempfunctionPtr_time - Tempfunction_time 应该是常数
| N******K 发帖数: 10202 | 4 开销应该是常数 和函数具体内容(N)无关
太诡异了
【在 n*****t 的大作中提到】 : 高手没空,蓝翔技校试着回答: : 间接调用开销稍大,不过貌似不该差那么多
| n*****t 发帖数: 22014 | 5 神马系统?运行时间长了,被中断次数多了?N 放大 100 倍看看?
【在 N******K 的大作中提到】 : 开销应该是常数 和函数具体内容(N)无关 : 太诡异了
| a*****e 发帖数: 1700 | 6 编译器的问题。用 clang++ 试了一下,两个时间是一样的。
如果用 clang++ -O2 的化,还需要打印 Value 的值,否则整个计算被忽略掉了。 | n*****t 发帖数: 22014 | 7 艾玛,这编译器胆子够大的 LOL
【在 a*****e 的大作中提到】 : 编译器的问题。用 clang++ 试了一下,两个时间是一样的。 : 如果用 clang++ -O2 的化,还需要打印 Value 的值,否则整个计算被忽略掉了。
| a*****e 发帖数: 1700 | 8 其实这个没什么,只要能够确定(1)循环必然终止(2)无副作用(3)无返回值,那么这段
代码属于 dead code。常见的编译器都有这么一个 pass,功能差不多。
【在 n*****t 的大作中提到】 : 艾玛,这编译器胆子够大的 LOL
| a***n 发帖数: 538 | 9 用volatile double就一样了。第一种情况可能优化的时候用了register,因为a的地址
在编译的时候就知道了吧。 | w*******e 发帖数: 285 | 10 this is because in release mode, the first function call is in-lined, and
the second one to call the function pointer is not in-lined by vs compiler.
【在 N******K 的大作中提到】 : 编译器 vs2013 release模式 : Tempfunction 计算一个数然后输出 N 控制计算复杂度 : 我用function pointer :TempfunctionPtr = &Tempfunction : 然后比较 直接调用函数Tempfunction 和 通过指针间接调用函数 : 结果很诡异 : N=1000 , Tempfunction_time=1, TempfunctionPtr_time=2; : N=2000 , Tempfunction_time=2, TempfunctionPtr_time=6; : N=4000 , Tempfunction_time=4, TempfunctionPtr_time=11; : N=8000 , Tempfunction_time=8, TempfunctionPtr_time=21; : TempfunctionPtr_time - Tempfunction_time 应该是常数
| d*****u 发帖数: 43 | 11 The difference is from inline optimization of the compiler.
When the function is inlined, the compiler has more information and
opportunities to optimize the code. At inlining, the compiler can emit
different code for the inner most loop body a+=i; .
On my VS2013, a+=i is as follows in Tempfunction():
movsd xmm0, QWORD PTR [eax] // load a
addsd xmm0, xmm1 // a += i
addsd xmm1, xmm2 // i += 1
movsd QWORD PTR [eax], xmm0 // save a
comisd xmm3, xmm1 // cmp N, i
ja SHORT $LL3@Tempfuncti
While in main(), when inlined, a+=i is like:
movapd xmm0, xmm1 // temp = i
addsd xmm1, xmm3 // i += 1
addsd xmm2, xmm0 // a += temp
comisd xmm4, xmm1 // cmp N, i
ja SHORT $LL13@main
You can see that the difference is that in Tempfunction() has to load and
save "a" for every iteration. In main() after inlining the compiler knows
that "a" is not used otherwise, so it is free to use a register for all
iterations, and only save to memory when the loop is done.
The execution time of non-inline code is 1000000*(N*T1+T2), the inline code
1000000*(N*T3+T4).
If T1==T3, the difference will be 1000000*(T2-T4), which does not depend on
N. This assumes the only difference is from the function call overhead. This
is obviously not true according to your timing data.
The actual difference has two parts: 1000000*N*(T1-T3) + 1000000*(T2-T4).
Given your timing data, it seems that T1~=2.5e-9, T2~=1e-6, T3~=1e-9, T4~=0.
The load/save part is a big deal; it roughly accounts for 1.5e-9, making non
-inlined version 150% slower for the inner loop body.
PS: If the function chooses to use a pass-by-value "a" then returns "a", the
loop body will not have the load/save overhead.
PS2: The code should use int i=0 as the loop variable in Tempfunction() in
this example. Using double i prevents the compiler from using loop unrolling
, because double is not exact to represent arbitrary integer and the
compiler has to be faithful to this choice that the code writer decides to
take. Now if you use int i, VS2013 tries hard to unroll the loop and creates
very complex assembly code that also handles corner cases for N that is not
a multiple of chosen loop unrolling factor. | N******K 发帖数: 10202 | 12 多谢 这个解释详细
关于:
PS: If the function chooses to use a pass-by-value "a" then returns "a", the
loop body will not have the load/save overhead.
我测试过 如果这样的话 两种调用方式的时间是一样的
【在 d*****u 的大作中提到】 : The difference is from inline optimization of the compiler. : When the function is inlined, the compiler has more information and : opportunities to optimize the code. At inlining, the compiler can emit : different code for the inner most loop body a+=i; . : On my VS2013, a+=i is as follows in Tempfunction(): : movsd xmm0, QWORD PTR [eax] // load a : addsd xmm0, xmm1 // a += i : addsd xmm1, xmm2 // i += 1 : movsd QWORD PTR [eax], xmm0 // save a : comisd xmm3, xmm1 // cmp N, i
| N******K 发帖数: 10202 | 13 修改为如下
void Tempfunction(double& a, int N)
{
a = 0;
double b = 0;
for (double i = 0; i < N; ++i)
{
b += i;
//a += i;
}
a = b;
}
结果
N=1000 , Tempfunction_time=1, TempfunctionPtr_time=1;
N=2000 , Tempfunction_time=2, TempfunctionPtr_time=2;
N=4000 , Tempfunction_time=4, TempfunctionPtr_time=4;
N=8000 , Tempfunction_time=8, TempfunctionPtr_time=8;
【在 N******K 的大作中提到】 : 编译器 vs2013 release模式 : Tempfunction 计算一个数然后输出 N 控制计算复杂度 : 我用function pointer :TempfunctionPtr = &Tempfunction : 然后比较 直接调用函数Tempfunction 和 通过指针间接调用函数 : 结果很诡异 : N=1000 , Tempfunction_time=1, TempfunctionPtr_time=2; : N=2000 , Tempfunction_time=2, TempfunctionPtr_time=6; : N=4000 , Tempfunction_time=4, TempfunctionPtr_time=11; : N=8000 , Tempfunction_time=8, TempfunctionPtr_time=21; : TempfunctionPtr_time - Tempfunction_time 应该是常数
|
|