2014-02-06 75 views
2

的开销时间的变化在这里是C++代码,我用vs2013,释放模式约函数指针:为什么当函数的内容改变

#include <ctime> 
#include <iostream> 

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个从1000的值更改为8000,并记录Tempfunction_time和TempfunctionPtr_time。 结果是怪异:

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不是恒定的, 和TempfunctionPtr_time = 2〜3 ​​ Tempfunction_time。 差异应该是一个常数,它是函数指针的开销。

出了什么问题?

编辑:

假设VS2013内联Tempfunction如果它通过Tempfunction(所谓的),并且不内联,如果它是由(* TempfunctionPtr)调用,那么我们就可以解释的差异。所以,如果这是真的,为什么不能编译器内联(* TempfunctionPtr)?

+1

我现在看到你在发布模式下构建它。我确信现在是优化。关掉所有可能的优化(我不知道该怎么做vs 2013),然后再试一次。 –

+1

std :: time与秒一起工作,也许你需要毫秒精度的措施。 – Narkha

+0

实际上,您可以在Windows上使用QueryPerformanceCounter。 –

回答

0

我在Linux机器上用g ++编译了现有代码,发现时间太短而无法在几秒钟内精确测量,因此重写了它以使用std::chrono更精确地测量时间 - 我也必须“使用“变量Value(因此在下面打印”499500“),否则编译器会完全优化掉第一个循环。然后,我得到以下结果:

Tempfunction_time = 1.47983 
499500 
TempfunctionPtr_time = 1.69183 
499500 

现在,结果我是GCC(!4.6.3版本 - 其他版本可供选择,并可能给其他结果),这是不一样的编译器为微软,因此结果可能会有所不同 - 不同的编译器有时会以不同的方式优化代码。我真的很惊讶,编译器不知道TempFunction的结果只需要计算一次。但是,嘿,让它更容易编写没有欺骗的基准。我的第二个观察结果是,在我的编译器中,如果我用代码围绕主代码替换int N=1000;,这两种情况之间没有或很少有区别 - 我不完全确定为什么,因为代码看起来完全相同(没有通过函数指针调用,因为编译器知道函数指针是一个常量),并且在两种情况下内联都是内联的。 (当N等于1000时,发生相同的“相等” - 所以我很难确定这里发生了什么......

要实际测量函数指针和直接函数调用之间的差异,将需要移动TempFUnction到一个单独的文件中,“隐藏”存储在TempFunctionPtr使得编译器不搞清楚你在做什么实际的价值。

最后,我结束了这样的事情:

typedef void (*FunPtr)(double &a, int N); 

void Tempfunction(double& a, int N) 
{ 
    a = 0; 
    for (double i = 0; i < N; ++i) 
    { 
    a += i; 
    } 
} 

FunPtr GetFunPtr() 
{ 
    return &Tempfunction; 
} 

而且这样的 “主” 代码:

#include <iostream> 
#include <chrono> 

typedef void (*FunPtr)(double &a, int N); 

extern void Tempfunction(double& a, int N); 
extern FunPtr GetFunPtr(); 

int main() 
{ 
    for(int N = 1000; N <= 8000; N *= 2) 
    { 
    std::cout << "N=" << N << std::endl; 
    double Value = 0; 
    auto t0 = std::chrono::system_clock::now(); 
    for (int i = 0; i < 1000000; ++i) 
    { 
     Tempfunction(Value, N); 
    } 
    auto t1 = std::chrono::system_clock::now();; 
    std::chrono::duration<double> Tempfunction_time = t1-t0; 
    std::cout << "Tempfunction_time = " << Tempfunction_time.count() << '\n'; 
    std::cout << Value << std::endl; 

    auto TempfunctionPtr = GetFunPtr(); 

    Value = 0; 
    t0 = std::chrono::system_clock::now(); 
    for (int i = 0; i < 1000000; ++i) 
    { 
     (*TempfunctionPtr)(Value, N); 
    } 
    t1 = std::chrono::system_clock::now(); 
    std::chrono::duration<double> TempfunctionPtr_time = t1-t0; 
    std::cout << "TempfunctionPtr_time = " << TempfunctionPtr_time.count() << '\n'; 
    std::cout << Value << std::endl; 
    } 
} 

然而,差异是千分之一秒,变体是一个明显的赢家,唯一的结论是明显的,“调用一个函数比内联它慢”。

N=1000 
Tempfunction_time = 1.78323 
499500 
TempfunctionPtr_time = 1.77822 
499500 
N=2000 
Tempfunction_time = 3.54664 
1.999e+06 
TempfunctionPtr_time = 3.54687 
1.999e+06 
N=4000 
Tempfunction_time = 7.0854 
7.998e+06 
TempfunctionPtr_time = 7.08706 
7.998e+06 
N=8000 
Tempfunction_time = 14.1597 
3.1996e+07 
TempfunctionPtr_time = 14.1577 
3.1996e+07 

当然,如果我们这样做“只有一半的隐藏绝招”,使功能是已知的,在第一种情况下inlineable,而不是已知的,通过函数指针,我们或许可以期待一个区别。但通过指针调用函数本身并不昂贵。当编译器决定内联函数时,真正的区别就来了。

显然,这些是GCC 4.6.3的结果,它与MSVS2013不同。您应该对上述代码中的“时间序列”进行修改,然后查看它有什么不同。

+0

谢谢。我在Windows上得到了类似的结果。我认为vs2013为我的代码发布了Tempology。而且,为什么内联可以提供非恒定的性能提升?即,而不是Tempfunction_time - TempfunctionPtr_time =常数,我得到Tempfunction_time = 2〜3 ​​ TempfunctionPtr_time? – liangbright

+0

另一个问题:如果编译器被函数指针调用,它不能内联一个函数吗? (例如,虚函数) – liangbright

+0

@ liangbright如果编译器可以证明该方法未被覆盖,它仍然可以内联。现在编译器变得越来越聪明了。那么现在甚至可以跨文件内联。 –