2017-02-09 45 views
3

Скажем, у меня есть утверждают() что-то вроде assert(x < limit); я смотрел на поведение оптимизатора в GDC в выпуске и отладочных сборок со следующим фрагментом кода:утверждают() s, оптимизация и директива предположим() в D

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-tests, присутствовал в коде выпуска, поскольку мы должны потерять все эти накладные расходы.

В идеале я хотел бы выразить это условие в смысле передачи информации компилятору независимо от релизов/отладок, об условиях, которые всегда можно считать истинными, и поэтому такие предположения могут направлять оптимизацию в очень мощные способы.

Я считаю, что некоторые компиляторы C++ имеют нечто, называемое __assume() или некоторые из них, но память мне не помогает. GCC имеет специальную директиву __builtin_unreachable(), которая может быть использована для создания функции accept(). В принципе, если бы я мог построить свою собственную директиву accept(), это могло бы повлиять на утверждение определенных истин о известных значениях или известных диапазонах и публикацию их на пути оптимизации независимо от режима release/отладки, но без создания какого-либо реального кода для accept() в сборке релиза, тогда как в режиме отладки он будет точно таким же, как assert().

Я пробовал эксперимент, который вы видите в cxx2, который всегда оптимизирует оптимизацию, так что там хорошая работа, но он генерирует то, что является морально отладочным кодом для if if-условия accept() даже в режиме выпуска с тестом и условный переход к неопределенной инструкции, чтобы остановить процесс.

Есть ли у кого-нибудь идеи о том, разрешимо ли это? Или вы думаете, что это полезный компилятор F-компилятора?

ответ

0

ОК, я действительно не знаю, чего вы хотите? cxx2 это решение some more info

+0

cxx2 содержит сгенерированный код для if-состояния даже в режиме деблокирования. Цель состоит в том, чтобы пропустить этот код_, как это происходит с кодом проверки состояния в assert. Это то, что я сказал ранее, но, возможно, я не дал понять. –

+0

На самом деле, получить доступ к встроенной встроенной консоли GCC может это сделать, но тогда это полезно только для одного компилятора, но в конце концов, это касается оптимизации. –

+0

Я забыл спросить, как я могу скомпоновать решение cxx2 на что-то опрятное в форме 'accept (cond)', предполагая, что мы не можем сделать ничего лучше. Я понятия не имею, как это сделать без препроцессора, отсутствие опыта работы с D. –

4

Насколько я знаю, __builtin_unreachable следующая лучшая замена для assume как функции в GCC. В некоторых случаях условие if все еще не может быть оптимизировано: "Assume" clause in 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; 
} 

Есть две интересные подробности здесь:

  1. Нам не нужны струнные Примеси или, подобным образом сложные вещи. Пока вы компилируете с помощью -O, GDC полностью оптимизирует вызов функции.
  2. Для этого необходимо выполнить функцию assume. К сожалению, встроенные нормальные функции не полностью поддерживаются, когда assume находится в другом модуле как вызывающая функция. В качестве обходного пути мы используем шаблон с аргументами 0 шаблонов. Это должно гарантировать, что встраивание всегда может работать.

Вы можете проверить и изменить этот пример здесь: explore.dgnu.org

Теперь мы (разработчики GDC) могут легко переписать assert(...) в if(...) __builtin_unreachable() в режиме выпуска. Но это может сломать некоторый код, поэтому dmd должен реализовать это в первую очередь.

+0

Я думаю, что переписывание утверждений в предложенном способе было бы целесообразным изменением, созданием лучшего кода и согласием, что dmd должен сделать это первым, если кто-то это сделает. Однако утверждать/предполагать или что-то еще не должно допускать побочных эффектов в состоянии - это уже может быть верно для assert, я не проверял. –

+0

спасибо пользователю jpf за отличный вклад –

+0

Я бы, вероятно, добавил 'assert()' в начале jpf 'accept()', чтобы вы получили правильное поведение в отладочных сборках. –