Почему constexpr работает хуже обычного выражения? - PullRequest
0 голосов
/ 12 июля 2020

Я знаю, что есть похожий вопрос по этому поводу: constexpr работает хуже во время выполнения .
Но мой случай намного проще, чем тот, и ответов мне было недостаточно. Я только изучаю constexpr в C ++ 11, и я написал код, чтобы сравнить его эффективность, и по какой-то причине использование 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 для компиляции проекта Visual Studio, созданного с помощью следующего кода CMake:

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 Ответ

2 голосов
/ 12 июля 2020

Без оптимизации компилятор сохранит вызов 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:

Таким образом, оба результата приведут к одинаковому машинному коду, содержащему только подсчет l oop и больше ничего. Поэтому, как только вы включите оптимизацию, вы будете измерять только l oop, но ничего не связанное с constexpr.

С -O2 даже l oop будет оптимизировано, и вы будете измерять только :

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