【问题标题】:constexpr vs repeated function calls performanceconstexpr 与重复的函数调用性能
【发布时间】:2019-03-16 13:53:02
【问题描述】:

我正在测试下面的代码来检查constexpr 的性能。对于第一次迭代,结果符合预期。但是对于下一次迭代,正常的函数调用有时会胜过constexpr。我在这里想念什么?我希望constexpr 电话在任何情况下都会更好。在第一次迭代后,正常函数所花费的时间也减少了。如何解释这种行为?

可以看到代码结果here

编辑:如果我取消注释以下代码中的行,为每次迭代提供不同的值来求和,结果仍然相似。你可以看到它的结果here

Edit2:我尝试了@geza 更改,并为第一个函数调用执行了 300 万次操作,并为第二个函数调用执行了3 Million + i。我期望 constexpr 花费的时间非常少(几乎与 100 所花费的时间相同),但它花费的时间与 non-constexpr 函数一样多。结果link

#include <iostream>
#include <chrono>

using namespace std;
using namespace std::chrono;

constexpr long long sum(const int* n){
    long long sum = 0;
    for(int i = 1; i <= *n; i++){
        sum += i;
    }
    return sum;
}

long long sum(int*  n){
    cout<<"Calling sum\n";
    long long sum = 0;
    for(int i = 1; i <= *n; i++){
        sum += i;
    }
    return sum;
}


int main(void){
    const int* p;
    int a = 100;
    p = &a;
    
    int *p1;
    p1 = &a;
    
    for(int i = 0; i < 10; i++){

        /*
        int* p1;
        int b = 100 + i; //If I uncomment the lines here are remove the 
        p1 = &b;         //earlier p1 declaration. Still the results are similar
        */
    
        auto start = high_resolution_clock::now(); 
        cout<<sum(p1)<<endl;
        auto stop = high_resolution_clock::now();
        cout<<"Time taken Non constexpr: "<<duration_cast<microseconds>(stop - start).count()<<endl;
        
        start = high_resolution_clock::now(); 
        cout<<sum(p)<<endl;
        stop = high_resolution_clock::now();
        cout<<"Time taken constexpr: "<<duration_cast<microseconds>(stop - start).count()<<endl;
    }
}

我的代码很简单,创建两个指针,一个是常量(p),另一个不是(p1)。当用p1 调用sum() 时,没有constexpr 的函数被调用,这可以通过 print "Calling sum 看到,当用p 调用时,带有constexpr 的函数被调用调用,可以看成什么都没有打印出来。

结果

$g++ -o main *.cpp
$主
调用 sum
5050
所用时间非 constexpr:63
5050
所用时间 constexpr:7

调用 sum
5050
所用时间非 constexpr:5
5050
所用时间 constexpr:6
.
.
.
.
调用 sum
5050
所用时间非 constexpr:2
5050
constexpr 所用时间:6

.
.
.
.
调用 sum
5050
所用时间非 constexpr:2
5050
constexpr 所用时间:2

【问题讨论】:

  • constexpr 不是性能优化。您将它用于需要/可以在编译时完成的事情。
  • 缓存问题?对于基准测试,始终启用优化。并且不要将输出包含在您的时间安排中。
  • 您可能应该检查编译器为机器代码生成的内容。请注意,您的函数调用的结果与计时循环无关,因此编译器可以对其进行优化,以使其不计算多次?
  • 如果使用非编译时间常量调用,constexpr 函数的行为类似于非 constexpr 函数。想一想,使用运行时参数,您无法进行编译时评估。也正如其他人评论的那样,输出操作是主要部分。
  • 更改编译器选项并添加-O2。没有人关心未优化代码的性能。

标签: c++ performance repeat constexpr


【解决方案1】:

您的测量结果有误。您的时间包括打印到stdout。修改您的程序以正确测量时间(并从sum 中删除cout):

auto start = high_resolution_clock::now(); 
auto r = sum(p1);
auto stop = high_resolution_clock::now();
cout<<r<<endl;
cout<<"Time taken Non constexpr: "<<duration_cast<microseconds>(stop - start).count()<<endl;

您会注意到,在(几乎)所有情况下,时间都是 0。

(对原始程序的解释:似乎第一次调用cout &lt;&lt; 比其他调用花费更多时间。很可能cout 进行了某种延迟初始化,这就是为什么第一次调用是慢一点。)

请注意,由于您不在constexpr 上下文中调用sum(const int *),它将被用作“正常”函数调用,就像sum(int *)

【讨论】:

  • 我认为,它不是第二个函数中的cout&lt;&lt;"Calling sum\n";。它看起来像auto r = sum(p1) 和单独打印的cout&lt;&lt;r&lt;&lt;endl;。如果我们对第二次调用做同样的事情,它也会全为零。现在这更奇怪了。但它很好,因为一切都是零。
  • @HariUserX:也许是endl,它会刷新,触发缓慢(仅限第一次。我的意思是,第一次实际打印很慢)。
  • 我尝试了你的更改 3000000。同样的结果。
  • @HariUserX:同样的结果是什么意思?请注意,在测量时间时,您需要确保 CPU 具有恒定的频率。通常,操作系统/CPU 需要一点时间来提高时钟速度。
  • @HariUserX:请阅读我的回答:“请注意,由于您不在 constexpr 上下文中调用 sum(const int *) ,它将被用作“正常”函数调用,只是像总和(int *)。”。这就是为什么没有区别。请注意,如果启用优化,您可能会获得与 constexpr 函数(在 constexpr 上下文中调用)相同的非 constexpr 函数代码。在测量速度时启用优化至关重要。
【解决方案2】:

我建议您启用优化,然后重新运行您的基准测试。 C++,尤其是库,依赖于编译器优化来生成快速的汇编代码。优化器可以剥离库抽象,然后生成更好的代码。

您链接的网站允许您更改编译器选项,只需添加 -O2 以设置优化级别 2 并重新运行基准测试。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-09-18
    • 2018-07-10
    • 2021-06-26
    • 1970-01-01
    • 2019-12-21
    • 1970-01-01
    • 2018-01-16
    相关资源
    最近更新 更多