Хочу кое что уточнить. То что я писал выше относится к стандарту C++17. В C++20 приняли "two's complement". Это раздел 6.8.1, параграф 3. То есть код из начала статьи:
Нет там никакого переполнения. Просто одни и те же биты интерпретируются по-разному.
@screwer Уточните п-та, какой параграф стандарта С++ гарантирует интерпретацию битов по разному в случае знаковых типов? Ссылку на драфт я привел в конце статьи.
Очень рекомендую почитать Hacker Delight, чтобы не делать подобных "открытий".
Как будто c variadic functions должно работать. Я к тому, что еще до вызова variadic functions можно прикинуть сколько места нужно для возвращаемого значения.
Быть может, я упускаю из вида, какие-то особые платформы, но кажется, что уже есть целый ряд случаев, когда любой современный компилятор сделает NRVO. Надеюсь на предложение Антона обратят внимание...
Сейчас ни один компилятор не поддерживает все описанные в paper кейсы. Я отправлял патч в Clang, чтобы покрыть на 20% больше кейсов, чем поддерживается сейчас, но до конца не добил тему, потому что патчи медленно принимаются.
А что за возражения? По делу или просто не обращают внимание на патч?
Думаю, в одной функции может быть, чтоб иногда было RVO, иногда NRVO, а иногда было вызвано копирование. Т.е. если говорить языком стандарта, то и RVO, и NRVO это частные случаи отмены копирования (copy elision). Крайняя точка принятия решения о том, случится ли отмена копирования - это точка вызова ф-ции в Run Time. Технически даже можно представить, что в ряде ситуаций, компилятор может оставить место под результат работы функции на стеке и начать ее исполнять. И уже позже, создать в оставленном месте результат работы функции. А если так не будет получатся, то по возможности выполнить перемещение в оставленное место. А если и это не возможно, то копирование.
Что-то я такого атрибута ни где найти не могу. Это пример, как могло бы выглядеть? Вообщем ссылочку пришлите п-та, если знаете про такой стандартный или не стандартный атрибут.
Наоборот, было бы полезно иметь "вот здесь должен быть rvo", "вот здесь только хвостовая рекурсия", если разработчик переписывает код и это уже не получается гарантировать, то бить по рукам, пусть думает дальше.
Да, это отличный подход! Много таких штук в C++, которые не понятно сработают или нет.
Вижу мне уже вроде намекают, что такие атрибуты есть. Не знаю, я вообще на C++-сник уже давно.
Если не сложно, уточните п-та, о чем речь. Мне про них не известно, а если есть я бы хотел знать. Если информация не очень точная, можно и в личку. Спасибо!
Ведь уже появился синтаксис атрибутов, что мешает наконец завести атрибуты для включения гарантий нужного поведения?
Тут традиционный компромис:
Гарантированное поведение на одной чаше весов.
На другой, эффективность на разных платформах.
С++ решает обычно решает такие штуки в строну эффективности. С другой стороны, синтаксис языка и так сложен. Кажется, что нежелание добавлять еще опций по указанию таких штук (например, через атрибуты), тоже можно понять. Думаете пользовался бы популярностью атрибут "Не применять RVO/NRVO"?
Ну и в дополнение, можно вспомнить о том, как принимаются решения о добавлении новых особенностей в C++ :)
Как следствие получаем C++, как весьма сложную конструкцию, с очень высоким порогом входа. На мой взгляд, сейчас язык непомерно сложен, но стал по современнее последние годы.
кажется, что RVO для компилятора понятнее и удобнее, чем NRVO.
Похоже что так. RVO от части перекочевало из оптимизации в правила обязательной отмены копирования. Похоже, тенденция от стандарта к стандарту в том, чтоб уменьшать серую зону, в которой компилятор сам решает применять ли RVO/NRVO или нет.
поэтому обычно считается, что после проверки if(/condition/), быстрый возврат return {}; — это хорошо.
Кроме того, отмечу, что при таком подходе (return early pattern), код становится менее громоздким и часто лучше читается, на мой взгляд.
вроде бы, он хорош со всех сторон. но получается, что шансы на получения оптимизации для нас уменьшаются?
Кажется, что да. Я проверил на clang (MacOS X). На нашем втором примере (где в двух местах return local_variable; ) NRVO был применен. В нашем первом примере, если вызвать вот так:
NRVOCheck f(int k) {
if (k == 3)
return NRVOCheck();
NRVOCheck local_variable;
// Действия с local_variable
return local_variable;
}
f(2);
Как мне кажется тут как раз серая зона. RVO/NRVO может сработать, а может и нет. Зависит от компилятора, платформы и даже еще от одного факта. От того, всегда или нет выполнено условие /*condition*/. Если выполнено всегда, компилятор может понять это, и оптимизировать код, оставив одну ветку if().
Думаю, что если мы перепишем пример так:
C f() {
C local_variable;
if(/*condition*/)
return local_variable;
// Действия с local_variable
return local_variable;
}
O-большое, это с точность до константы, на которую умножается асимптотически заданное кол-ва операций (или кол-во какого-то другого ресурса. Памяти, например). Если в программе используются последовательно 2 алгоритма сложностью O(log n), то в итоге получим сложность O(log n). Так же замечу, что в общем случае сортировать массив за O(log n) не выйдет. Скорее это выйдет за O(n log n).
Спасибо! Интересный документ.
Спасибо за комментарии!
Хочу кое что уточнить. То что я писал выше относится к стандарту C++17. В C++20 приняли "two's complement". Это раздел 6.8.1, параграф 3. То есть код из начала статьи:
должен всегда работать одинаково и
signed_offset
в C++20 должно быть -7, как я понимаю.Спасибо огромное! Поправил.
Ага. Как вариант.
@screwer Уточните п-та, какой параграф стандарта С++ гарантирует интерпретацию битов по разному в случае знаковых типов? Ссылку на драфт я привел в конце статьи.
Спасибо, обязательно почитаю!
Как будто c variadic functions должно работать. Я к тому, что еще до вызова variadic functions можно прикинуть сколько места нужно для возвращаемого значения.
А в чем сомнения по поводу __stdcall?
Спасибо за ответ и за комментарий! Я посмотрю патч и внимательнее прочитаю предложение Антона. Интересно.
Быть может, я упускаю из вида, какие-то особые платформы, но кажется, что уже есть целый ряд случаев, когда любой современный компилятор сделает NRVO. Надеюсь на предложение Антона обратят внимание...
А что за возражения? По делу или просто не обращают внимание на патч?
Спасибо за уточнение.
Думаю, в одной функции может быть, чтоб иногда было RVO, иногда NRVO, а иногда было вызвано копирование. Т.е. если говорить языком стандарта, то и RVO, и NRVO это частные случаи отмены копирования (copy elision). Крайняя точка принятия решения о том, случится ли отмена копирования - это точка вызова ф-ции в Run Time. Технически даже можно представить, что в ряде ситуаций, компилятор может оставить место под результат работы функции на стеке и начать ее исполнять. И уже позже, создать в оставленном месте результат работы функции. А если так не будет получатся, то по возможности выполнить перемещение в оставленное место. А если и это не возможно, то копирование.
Что-то я такого атрибута ни где найти не могу. Это пример, как могло бы выглядеть? Вообщем ссылочку пришлите п-та, если знаете про такой стандартный или не стандартный атрибут.
Да, это отличный подход! Много таких штук в C++, которые не понятно сработают или нет.
Если не сложно, уточните п-та, о чем речь. Мне про них не известно, а если есть я бы хотел знать. Если информация не очень точная, можно и в личку. Спасибо!
Такой, кажется, что мог бы использоваться. Для уверенности, что сработает.
Да. Есть такое дело :)
Тут традиционный компромис:
Гарантированное поведение на одной чаше весов.
На другой, эффективность на разных платформах.
С++ решает обычно решает такие штуки в строну эффективности. С другой стороны, синтаксис языка и так сложен. Кажется, что нежелание добавлять еще опций по указанию таких штук (например, через атрибуты), тоже можно понять. Думаете пользовался бы популярностью атрибут "Не применять RVO/NRVO"?
Ну и в дополнение, можно вспомнить о том, как принимаются решения о добавлении новых особенностей в C++ :)
Как следствие получаем C++, как весьма сложную конструкцию, с очень высоким порогом входа. На мой взгляд, сейчас язык непомерно сложен, но стал по современнее последние годы.
Похоже что так. RVO от части перекочевало из оптимизации в правила обязательной отмены копирования. Похоже, тенденция от стандарта к стандарту в том, чтоб уменьшать серую зону, в которой компилятор сам решает применять ли RVO/NRVO или нет.
Кроме того, отмечу, что при таком подходе (return early pattern), код становится менее громоздким и часто лучше читается, на мой взгляд.
Кажется, что да. Я проверил на clang (MacOS X). На нашем втором примере (где в двух местах
return local_variable;
) NRVO был применен. В нашем первом примере, если вызвать вот так:NRVO не сработал.
Как мне кажется тут как раз серая зона. RVO/NRVO может сработать, а может и нет. Зависит от компилятора, платформы и даже еще от одного факта. От того, всегда или нет выполнено условие
/*condition*/
. Если выполнено всегда, компилятор может понять это, и оптимизировать код, оставив одну веткуif()
.Думаю, что если мы перепишем пример так:
шансов на NRVO прибавится.
O-большое, это с точность до константы, на которую умножается асимптотически заданное кол-ва операций (или кол-во какого-то другого ресурса. Памяти, например). Если в программе используются последовательно 2 алгоритма сложностью O(log n), то в итоге получим сложность O(log n). Так же замечу, что в общем случае сортировать массив за O(log n) не выйдет. Скорее это выйдет за O(n log n).
Вероятно, добавлю его в статью.
Спасибо!
> Что подразумевается под платформой Intel?
Я полагал, что Intel пишет TBB для своих процессоров. Intel x86, вероятно эта штука будет корректно работать и для AMDx86. Это то, что я имел ввиду.
Но что вы правы. У TBB список совместимости шире, чем я думал. software.intel.com/content/www/ru/ru/develop/tools/threading-building-blocks.html
> Кстати, в большинстве дистрибутивов Linux для него есть предсобранные пакеты.
Вероятно, тогда там и с реализацией ExecutionPolicy может быть получше. Для мака пришлось собирать.
Интересно. Ни когда не задумывался об этом в таком ключе. Но видимо так и устроено. Так было бы логично.