提问者:小点点

为什么constexpr的表现比正常表达式差?


我知道还有一个类似的问题:constexpr在运行时性能更差。
但是我的情况要比那个简单得多,答案对我来说还不够。 我正在学习C++11中的constexpr,并编写了一段代码来比较它的效率,由于某种原因,使用constexpr使我的代码运行速度慢了4倍以上!
顺便说一下,我使用的示例与此站点中的示例完全相同:https://www.embarcados.com.br/introductao-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)

共1个答案

匿名用户

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

任何优秀的编译器都知道,对于给定的代码,与相关的所有内容都可以优化掉,而不会产生任何副作用。 因此所示代码永远无法给出值[i%size]=(2.0*3.0+10.0)/0.8;值[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;