【问题标题】:Why is constexpr performing worse than normal expression?为什么 constexpr 的性能比正常表达式差?
【发布时间】:2020-11-01 17:58:03
【问题描述】:

我知道有一个类似的问题:constexpr performing worse at runtime
但我的情况比那个简单得多,答案对我来说还不够。 我只是在 C++11 中学习 constexpr 并写了一个代码来比较它的效率,出于某种原因,使用 constexpr 使我的代码运行速度慢了 4 倍以上!
顺便说一句,我使用的示例与此站点中的示例完全相同:https://www.embarcados.com.br/introducao-ao-cpp11/(它是葡萄牙语,但您可以看到有关 constexpr 的示例代码)。已经尝试过其他表达方式,结果差不多。

constexpr double divideC(double num){
    return (2.0 * num + 10.0) / 0.8;
}

#define SIZE 1000
int main(int argc, char const *argv[])
{
    // Get number of iterations from user
    unsigned long long count;
    cin >> count;
    
    double values[SIZE];

    // Testing normal expression
    clock_t time1 = clock();
    for (int i = 0; i < count; i++)
    {
        values[i%SIZE] = (2.0 * 3.0 + 10.0) / 0.8;
    }
    time1 = clock() - time1;
    cout << "Time1: " << float(time1)/float(CLOCKS_PER_SEC) << " seconds" << endl;
    
    // Testing constexpr
    clock_t time2 = clock();
    for (int i = 0; i < count; i++)
    {
        values[i%SIZE] = divideC( 3.0 );
    }
    time2 = clock() - time2;
    cout << "Time2: " << float(time2)/float(CLOCKS_PER_SEC) << " seconds" << endl;

    return 0;
}

输入给定: 9999999999

输出:

> Time1: 5.768 seconds
> Time2: 27.259 seconds

谁能告诉我这是什么原因?由于 constexpr 计算应该在编译时运行,所以它应该更快地而不是更慢地运行此代码。

我正在使用 msbuild 版本 16.6.0.22303 来编译由以下 CMake 代码生成的 Visual Studio 项目:

cmake_minimum_required(VERSION 3.1.3)
project(C++11Tests)

add_executable(Cpp11Tests main.cpp)

set_property(TARGET Cpp11Tests PROPERTY CXX_STANDARD_REQUIRED ON)
set_property(TARGET Cpp11Tests PROPERTY CXX_STANDARD 11)

【问题讨论】:

  • 您是否在启用优化的情况下进行编译?
  • 是的,请确定编译器、版本、标准选项和完整的编译/链接命令行。
  • ...不,仅仅“Visual Studio 编译器”还远远不够。
  • 没有证据表明您在编译时启用了优化,如果不是,则性能测量没有任何意义。
  • 如果不进行优化,编译器将保留divideC 调用,因此速度较慢。通过对编译器的优化,可以知道与values 相关的所有内容都可以被优化掉而没有任何副作用。因此,显示的代码永远无法在 values[i%SIZE] = (2.0 * 3.0 + 10.0) / 0.8;values[i%SIZE] = divideC( 3.0 ); 的差异之间给出任何有意义的测量值

标签: c++ c++11 constexpr


【解决方案1】:

如果不进行优化,编译器将保留divideC 调用,因此速度较慢。

通过对任何体面的编译器进行优化,都知道 - 对于给定的代码 - 与 values 相关的所有内容都可以在没有任何副作用的情况下被优化掉。因此,显示的代码永远无法在values[i%SIZE] = (2.0 * 3.0 + 10.0) / 0.8;values[i%SIZE] = divideC( 3.0 ); 的差异之间给出任何有意义的测量

使用-O1,任何体面的编译器都会创建这样的东西:

    for (int i = 0; i < count; i++)
    {
        values[i%SIZE] = (2.0 * 3.0 + 10.0) / 0.8;
    }

结果:

        mov     rdx, QWORD PTR [rsp+8]
        test    rdx, rdx
        je      .L2
        mov     eax, 0
.L3:
        add     eax, 1
        cmp     edx, eax
        jne     .L3
.L2:

    for (int i = 0; i < count; i++)
    {
        values[i%SIZE] = divideC( 3.0 );
    }

结果:

        mov     rdx, QWORD PTR [rsp+8]
        test    rdx, rdx
        je      .L4
        mov     eax, 0
.L5:
        add     eax, 1
        cmp     edx, eax
        jne     .L5
.L4:

所以两者都会产生相同的机器代码,只包含循环的计数,没有其他内容。因此,一旦您打开优化,您将只测量循环,但与 constexpr 无关。

使用-O2,甚至循环也被优化掉了,你只需要测量:

    clock_t time1 = clock();
    time1 = clock() - time1;
    cout << "Time1: " << float(time1)/float(CLOCKS_PER_SEC) << " seconds" << endl;

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-10-08
    • 2023-03-07
    • 2011-01-11
    • 1970-01-01
    • 2016-07-29
    相关资源
    最近更新 更多