constexpr - разочарование
От: Дрободан Фрилич СССР  
Дата: 13.06.17 14:34
Оценка: 3 (1) -1
Категорически приветствую!

Игрался с констэкспрами.

template <typename T>
constexpr int my_log2(T n)
{
    return n? 1+my_log2(n>>1): -1;
}

int main()
{
    return my_log2<unsigned>(42);
}


И VC , и G++ генерят обычные вызовы даже в релизе.
Вычисления в compile time производятся только если компилятор припереть к стенке,
напр. передать параметром в шаблоне:
template <int I> struct eval_im
{
    enum { value = I };
};


Это нормально?
Это как гордая птица ёж, которая без пинка не полетит.

Еще вопрос. Поскольку на constexpr функции накладываются ограничения, ее реализация бывает неэффективна.
Можно ли сделать две версии функции constexpr и не constexpr, но с одинаковым именем и списком параметров?
Модератор-националист Kerk преследует оппонентов по политическим мотивам.
Отредактировано 13.06.2017 20:21 Bill Baklushi . Предыдущая версия .
Re: constexpr - разочарование
От: niXman Ниоткуда https://github.com/niXman
Дата: 13.06.17 14:39
Оценка:
хз, о чем вы...

мой компилятор всегда генерит mov eax, 5; ret;
пачка бумаги А4 стОит 2000 р, в ней 500 листов. получается, лист обычной бумаги стОит дороже имперского рубля =)
Re: constexpr - разочарование
От: night beast СССР  
Дата: 13.06.17 14:48
Оценка:
Здравствуйте, Дрободан Фрилич, Вы писали:

ДФ>Еще вопрос. Поскольку на constexpr функции накладываются ограничения, ее реализация бывает неэффективна.

ДФ>Можно ли сделать две версии функции constexpr и не constexpr, но с одинаковым именем и списком параметров?

нет.
Re: constexpr - разочарование
От: Conr Россия  
Дата: 13.06.17 18:25
Оценка: 2 (1) +2 -1
Здравствуйте, Дрободан Фрилич, Вы писали:

ДФ>Категорически приветствую!


ДФ>Игрался с констэкспрами.


ДФ>
ДФ>template <typename T>
ДФ>constexpr int my_log2(T n)
ДФ>{
ДФ>    return n? 1+my_log2(n>>1): -1;
ДФ>}

ДФ>int main()
ДФ>{
ДФ>    return my_log2<unsigned>(42);
ДФ>}
ДФ>


ДФ>И VC, и G++ генерят обычные вызовы даже в релизе.

constexpr не работает в обычных выражениях, попробуйте так:
int main()
{
    constexpr auto rrr = my_log2<unsigned>(42);
    return rrr;
}
Re[2]: constexpr - разочарование
От: Дрободан Фрилич СССР  
Дата: 13.06.17 19:05
Оценка:
niXman:

X>хз, о чем вы...


X>мой компилятор всегда генерит mov eax, 5; ret;


Вот скомпилёное 2017-й студией, релиз:
; Listing generated by Microsoft (R) Optimizing Compiler Version 19.11.25325.0 

    TITLE    c:\projects\vc2017\log2\log2.cpp
    .686P
    .XMM
    include listing.inc
    .model    flat

INCLUDELIB OLDNAMES

EXTRN    @__security_check_cookie@4:PROC
PUBLIC    ??$my_log2@I@@YAHI@Z                ; my_log2<unsigned int>
PUBLIC    _main
; Function compile flags: /Ogtp
;    COMDAT _main
_TEXT    SEGMENT
_main    PROC                        ; COMDAT
; File c:\projects\vc2017\log2\log2.cpp
; Line 6
    mov    ecx, 21                    ; 00000015H
    call    ??$my_log2@I@@YAHI@Z            ; my_log2<unsigned int>
    inc    eax
; Line 44
    ret    0
_main    ENDP
_TEXT    ENDS
; Function compile flags: /Ogtp
;    COMDAT ??$my_log2@I@@YAHI@Z
_TEXT    SEGMENT
??$my_log2@I@@YAHI@Z PROC                ; my_log2<unsigned int>, COMDAT
; _n$ = ecx
; File c:\projects\vc2017\log2\log2.cpp
; Line 6
    test    ecx, ecx
    je    SHORT $LN3@my_log2
    shr    ecx, 1
    call    ??$my_log2@I@@YAHI@Z            ; my_log2<unsigned int>
    inc    eax
; Line 7
    ret    0
$LN3@my_log2:
; Line 6
    or    eax, -1
; Line 7
    ret    0
??$my_log2@I@@YAHI@Z ENDP                ; my_log2<unsigned int>
_TEXT    ENDS
END
Модератор-националист Kerk преследует оппонентов по политическим мотивам.
Re[2]: constexpr - разочарование
От: Дрободан Фрилич СССР  
Дата: 13.06.17 19:10
Оценка:
Conr:

ДФ>>И VC, и G++ генерят обычные вызовы даже в релизе.

C>constexpr не работает в обычных выражениях, попробуйте так:
C>
C>int main()
C>{
C>    constexpr auto rrr = my_log2<unsigned>(42);
C>    return rrr;
C>}
C>


Да, так работает, спасибо.

_TEXT    SEGMENT
_main    PROC                        ; COMDAT
; File c:\projects\vc2017\log2\log2.cpp
; Line 44
    mov    eax, 5
; Line 45
    ret    0
_main    ENDP
Модератор-националист Kerk преследует оппонентов по политическим мотивам.
Re[3]: constexpr - разочарование
От: niXman Ниоткуда https://github.com/niXman
Дата: 13.06.17 19:12
Оценка: -2
Здравствуйте, Дрободан Фрилич, Вы писали:

ДФ>Вот скомпилёное 2017-й студией, релиз:


ну...сочувствую...
но вы бы еще tcc использовали и жаловались...

все проверенные мною компиляторы(gcc-6.3-linux, mingw-w64-7.1-windows, clang-4.0-linux, intel-17.0-linux) генерят одинаковый код, приведенный мною ранее.
пачка бумаги А4 стОит 2000 р, в ней 500 листов. получается, лист обычной бумаги стОит дороже имперского рубля =)
Отредактировано 13.06.2017 19:13 niXman . Предыдущая версия .
Re[4]: constexpr - разочарование
От: Conr Россия  
Дата: 13.06.17 19:39
Оценка: 1 (1)
Здравствуйте, niXman, Вы писали:

X>все проверенные мною компиляторы(gcc-6.3-linux, mingw-w64-7.1-windows, clang-4.0-linux, intel-17.0-linux) генерят одинаковый код, приведенный мною ранее.

Это хорошо, конечно. Но необязательно и зависит от ключей:

clang -O1: run time
clang -O2: compile time
gcc -O0: run time
gcc -O1: compile time
gcc -O2: compile time


Bjarne Stroustrup said on Jan 14, 2013 08:58 PM:

The correct answer — as stated by Herb — is that according to the standard a constexpr function may be evaluated at compiler time or run time unless it is used as a constant expression, in which case it must be evaluated at compile-time. To guarantee compile-time evaluation, we must either use it where a constant expression is required (e.g., as an array bound or as a case label) or use it to initialize a constexpr. I would hope that no self-respecting compiler would miss the optimization opportunity to do what I originally said: "A constexpr function is evaluated at compile time if all its arguments are constant expressions."

Re[5]: constexpr - разочарование
От: niXman Ниоткуда https://github.com/niXman
Дата: 13.06.17 19:48
Оценка:
Здравствуйте, Conr, Вы писали:

C>Это хорошо, конечно. Но необязательно и зависит от ключей:

т.к. в топике шла речь про релиз — я тестил релиз.

C>"A constexpr function is evaluated at compile time if all its arguments are constant expressions."

наш случай.
пачка бумаги А4 стОит 2000 р, в ней 500 листов. получается, лист обычной бумаги стОит дороже имперского рубля =)
Re[5]: constexpr - разочарование
От: Дрободан Фрилич СССР  
Дата: 13.06.17 20:09
Оценка:
Conr:

C>Это хорошо, конечно. Но необязательно и зависит от ключей:

C>

C>clang -O1: run time
C>clang -O2: compile time
C>gcc -O0: run time
C>gcc -O1: compile time
C>gcc -O2: compile time


Да, в корневом посте я допустил ошибку — под g++ компилировал без оптимизации — это не релиз.
С ключем -Ofast выражение вычислятся в compile time.
Модератор-националист Kerk преследует оппонентов по политическим мотивам.
Re: constexpr - разочарование
От: N. I.  
Дата: 13.06.17 20:30
Оценка: 18 (2)
Дрободан Фрилич:

ДФ>Еще вопрос. Поскольку на constexpr функции накладываются ограничения, ее реализация бывает неэффективна.

ДФ>Можно ли сделать две версии функции constexpr и не constexpr, но с одинаковым именем и списком параметров?

Пока что сделать две реализации одной функции для constexpr и не-constexpr контекстов вызова нельзя. Есть предложение ввести оператор constexpr: p0595r0.
Re[6]: constexpr - разочарование
От: N. I.  
Дата: 13.06.17 20:32
Оценка:
niXman:

C>>"A constexpr function is evaluated at compile time if all its arguments are constant expressions."

X>наш случай.

Фраза выдрана из контекста, это была всего лишь хотелка.
Re[6]: constexpr - разочарование
От: niXman Ниоткуда https://github.com/niXman
Дата: 13.06.17 21:01
Оценка:
Здравствуйте, Дрободан Фрилич, Вы писали:

ДФ>С ключем -Ofast выражение вычислятся в compile time.

проверял с -О2/-О3
пачка бумаги А4 стОит 2000 р, в ней 500 листов. получается, лист обычной бумаги стОит дороже имперского рубля =)
Re[7]: constexpr - разочарование
От: niXman Ниоткуда https://github.com/niXman
Дата: 13.06.17 21:02
Оценка:
Здравствуйте, N. I., Вы писали:

NI>Фраза выдрана из контекста, это была всего лишь хотелка.

эта хотелка уже работает во всех пртестированных мной компиляторах.
пачка бумаги А4 стОит 2000 р, в ней 500 листов. получается, лист обычной бумаги стОит дороже имперского рубля =)
Re[2]: constexpr - разочарование
От: Дрободан Фрилич СССР  
Дата: 13.06.17 21:03
Оценка: -3 :)
N. I.:

NI>Дрободан Фрилич:

NI>Пока что сделать две реализации одной функции для constexpr и не-constexpr контекстов вызова нельзя. Есть предложение ввести оператор constexpr: p0595r0.
У тебя странная фишка, вырезать при цитате "здравствуйте," и "вы писали".
Модератор-националист Kerk преследует оппонентов по политическим мотивам.
Re[8]: constexpr - разочарование
От: N. I.  
Дата: 14.06.17 08:15
Оценка: 5 (1)
niXman:

X>эта хотелка уже работает во всех пртестированных мной компиляторах.


Вот только не везде прослеживается связь оптимизации с объявлением функции как constexpr.

https://godbolt.org/g/ZgguUu
https://godbolt.org/g/uITdJo

Если определение функции доступно в каждой единице трансляции, где её вызывают (что типично для шаблонных и inline функций), то ничто не мешает компилятору попробовать вычислить её на этапе компиляции независимо от того, есть у неё спецификатор constexpr или нет. Наличие у функции спецификатора constexpr может лишь дать оптимизатору подсказку, что вероятность существования возможности посчитать её в compile time выше, чем в случае с "обычными" функциями, при этом в общем случае constexpr не даёт гарантий, что функцию можно посчитать в compile time при вызове её с любыми аргументами, значения которых известны на этапе компиляции. Например, здесь

template <typename T>
    constexpr int my_log2(T n)
{
    if (n <= 0)
        std::terminate();
    if (n == 1)
        return 0;
    return 1 + my_log2(n >> 1);
}

int main()
{
    constexpr int x = my_log2(42);
    constexpr int y = my_log2(0);
    std::cout << x << std::endl;
}

существует возможность вычислить my_log2(42) в compile time, но для my_log2(0) такой возможности нет — подобный вызов можно посчитать только в run time, несмотря на то, что в функцию передали вполне себе константный ноль.
Отредактировано 14.06.2017 8:17 N. I. . Предыдущая версия .
Re[9]: constexpr - разочарование
От: niXman Ниоткуда https://github.com/niXman
Дата: 14.06.17 08:57
Оценка:
Здравствуйте, N. I., Вы писали:

NI>Вот только не везде прослеживается связь оптимизации с объявлением функции как constexpr.


ну да, глупо использовать в constexpr-функции сисколы и жаловаться, что она не constexpr.
пачка бумаги А4 стОит 2000 р, в ней 500 листов. получается, лист обычной бумаги стОит дороже имперского рубля =)
Re[10]: constexpr - разочарование
От: N. I.  
Дата: 14.06.17 09:59
Оценка: 5 (1)
niXman:

NI>>Вот только не везде прослеживается связь оптимизации с объявлением функции как constexpr.


X>ну да, глупо использовать в constexpr-функции сисколы и жаловаться, что она не constexpr.


В том примере std::terminate ни на что принципиально не повлияло (присутствие такого вызова не делает функцию non-constexpr), GCC и Clang одинаково соптимизировали весь код до

mov     eax, 5
ret

как при наличии, так и при отсутствии спецификатора constexpr у функции.
Отредактировано 14.06.2017 10:12 N. I. . Предыдущая версия .
Re[5]: constexpr - разочарование
От: N. I.  
Дата: 14.06.17 12:27
Оценка:
Conr:

C>

The correct answer — as stated by Herb — is that according to the standard a constexpr function may be evaluated at compiler time or run time unless it is used as a constant expression, in which case it must be evaluated at compile-time. To guarantee compile-time evaluation, we must either use it where a constant expression is required (e.g., as an array bound or as a case label) or use it to initialize a constexpr. I would hope that no self-respecting compiler would miss the optimization opportunity to do what I originally said: "A constexpr function is evaluated at compile time if all its arguments are constant expressions."


Кстати, если рассматривать строго формальные гарантии, то даже в таком варианте

#include <exception>

template <typename T>
    constexpr int my_log2(T n)
{
    if (n <= 0)
        std::terminate();
    if (n == 1)
        return 0;
    return 1 + my_log2(n >> 1);
}

int main()
{
    constexpr int x = my_log2(42);
    using t = char[my_log2(43)];
    return x + sizeof(t);
}

вычисление my_log2(42) и my_log2(43) только на этапе трансляции стандартом не гарантировано. Observable behavior может быть одинаковым в любом случае — что при вычислении в compile time, что при вычислении в run time, как если бы программа была заменена на следующую:

#include <exception>

template <typename T>
    int my_log2(T n)
{
    if (n <= 0)
        std::terminate();
    if (n == 1)
        return 0;
    return 1 + my_log2(n >> 1);
}

int main()
{
    int x = my_log2(42);
    auto sizeof_t = my_log2(43);
    return x + sizeof_t;
}

Только в случае с

    constexpr int x = my_log2(42);
    using t = char[my_log2(43)];
    return x + sizeof(t);

компилятору всё же придётся как-то выяснять, что вызовы my_log2(42) и my_log2(43) удовлетворяют требованиям константных выражений, а значение my_log2(43) ещё и допустимо использовать в качестве размера массива.

Правило "as if" теоретически работает в обе стороны: компилятор волен не только "оптимизировать", но и "пессимизировать". В данном случае my_log2(42) и my_log2(43) могут быть (в теории) вычислены и в compile time (для определения валидности программы) и в run time (для определения значения, которое надо вернуть из main). Конечно, на практике вероятность встретить реализацию, которая делала бы такую "оптимизацию наоброт", почти нулевая.
Re[9]: constexpr - разочарование
От: Дрободан Фрилич СССР  
Дата: 14.06.17 16:15
Оценка:
N. I.:

NI>Если определение функции доступно в каждой единице трансляции, где её вызывают (что типично для шаблонных и inline функций), то ничто не мешает компилятору попробовать вычислить её на этапе компиляции независимо от того, есть у неё спецификатор constexpr или нет. Наличие у функции спецификатора constexpr может лишь дать оптимизатору подсказку, что вероятность существования возможности посчитать её в compile time выше, чем в случае с "обычными" функциями, при этом в общем случае constexpr не даёт гарантий, что функцию можно посчитать в compile time при вызове её с любыми аргументами, значения которых известны на этапе компиляции.


Тут может быть скрываться проблема совместимости. Компилятор A даёт послабления и съест исходник, а компилятор B выплюнет. Или даже две версии одного компилятора.
Программист, понадеявшийся на нестандартную фичу создаст крайне непортабельный код, который фиг откомпилируешь.

Конкретный пример, один компилятор проверяет все ветки на соответствие constexpr-у, а другой только те что вычисляются.
На хабре был пример https://habrahabr.ru/post/228181/ , бросать исключение в дохлых ветках констекспра формальное не запрещалось. Такой финт ушами использовался для совместимости compile-time и run-time по ошибкам.
Оказалось не все компиляторв такое могут.
Модератор-националист Kerk преследует оппонентов по политическим мотивам.
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.