assert()s, оптимизация и директива accept () в D
Скажем, у меня есть assert() что-то вроде assert( x < limit );
Я посмотрел на поведение оптимизатора в GDC в сборках выпуска и отладки со следующим фрагментом кода:
uint cxx1( uint x )
{
assert( x < 10 );
return x % 10;
}
uint cxx1a( uint x )
in { assert( x < 10 ); }
body
{
return x % 10;
}
uint cxx2( uint x )
{
if ( !( x < 10 ))
assert(0);
return x % 10;
}
Теперь, когда я строю в режиме отладки, утверждения имеют очень приятный эффект, вызывая огромную оптимизацию. GDC избавляется от ужасного кода, чтобы полностью выполнить операцию по модулю, потому что он знает о возможном диапазоне x из-за условия if в assert. Но в режиме релиза условие if отбрасывается, поэтому внезапно код ужаса возвращается, и больше нет никакой оптимизации ни в cxx1(), ни даже в cxx1a(). Это очень иронично, что режим выпуска генерирует гораздо худший код, чем код отладки. Конечно, никто не хочет, чтобы исполняемый код, принадлежащий if-тестам, присутствовал в коде выпуска, поскольку мы должны потерять все эти накладные расходы.
Теперь, в идеале, я хотел бы выразить условие в смысле передачи информации компилятору, независимо от сборок выпуска / отладки, об условиях, которые всегда можно считать истинными, и поэтому такие предположения могут направлять оптимизацию очень мощными способами.
Я полагаю, что некоторые компиляторы C++ имеют что-то, называемое __assume() или что-то подобное, но здесь мне не хватает памяти. GCC имеет специальную директиву __builtin_unreachable(), которая может использоваться для построения функции предположения (). По сути, если бы я мог создать свою собственную директиву предположить (), это дало бы эффект утверждения определенных истин об известных значениях или известных диапазонах и выставления / публикации их на этапах оптимизации независимо от режима выпуска / отладки, но без генерации какого-либо фактического кода вообще для условие accept () в сборке релиза, тогда как в режиме отладки оно будет точно таким же, как assert().
Я попробовал эксперимент, который вы видите в cxx2, который всегда запускает оптимизацию, так что это хорошая работа, но он генерирует то, что является морально отладочным кодом для if-условия accept () даже в режиме выпуска с тестом и условным переходом к неопределенная инструкция, чтобы остановить процесс.
У кого-нибудь есть идеи о том, разрешимо ли это? Или вы думаете, что это полезный элемент списка желаний фантазии компилятора D?
2 ответа
Насколько я знаю __builtin_unreachable
это следующая лучшая замена для assume
как функция в GCC. В некоторых случаях условие if все еще может не оптимизироваться: предложение "Assume" в gcc
Встроенные функции GCC доступны в GDC путем импорта gcc.builtins
, Вот пример того, как обернуть __builtin_unreachable
функция:
import gcc.builtins;
void assume()(bool condition)
{
if (!condition)
__builtin_unreachable();
}
bool foo(int a)
{
assume(a > 10);
return a > 10;
}
Здесь есть две интересные детали:
- Нам не нужны струнные миксины или подобные сложные вещи. Пока вы компилируете с
-O
В любом случае GDC полностью оптимизирует вызов функции. - Для этого работать
assume
функция должна быть встроена. К сожалению, встроенные нормальные функции не полностью поддерживаются, когдаassume
находится в другом модуле в качестве вызывающей функции. В качестве обходного пути мы используем шаблон с 0 аргументами шаблона. Это должно гарантировать, что встраивание всегда может работать.
Вы можете проверить и изменить этот пример здесь: https://explore.dgnu.org/
Теперь мы (разработчики GDC) могли легко переписать assert(...)
в if(...) __builtin_unreachable()
в режиме релиза. Но это может сломать некоторый код, поэтому dmd должен реализовать это первым.
ОК, я действительно не знаю, что вы хотите? cxx2
это решение немного больше информации