Могу ли я гарантировать, что компилятор C ++ не изменит порядок моих вычислений? - PullRequest
7 голосов
/ 05 октября 2011

Я сейчас читаю превосходную Библиотеку для арифметики Double-Double и Quad-Double , и в первых нескольких строках я замечаю, что они выполняют сумму следующим образом:

std::pair<double, double> TwoSum(double a, double b)
{
    double s = a + b;
    double v = s - a;
    double e = (a - (s - v)) + (b - v);
    return std::make_pair(s, e);
}

Вычисление ошибки, e, основано на том факте, что вычисление следует именно этому порядку операций из-за неассоциативных свойств математических операций с плавающей точкой IEEE-754.

Если я скомпилирую это в современном оптимизирующем компиляторе C ++ (например, MSVC или gcc), могу ли я быть уверен, что компилятор не оптимизирует способ выполнения этого вычисления?

Во-вторых, гарантируется ли это где-нибудь в стандарте C ++?

Ответы [ 10 ]

6 голосов
/ 05 октября 2011

Возможно, вы захотите взглянуть на страницу справочника g ++: http://gcc.gnu.org/onlinedocs/gcc-4.6.1/gcc/Optimize-Options.html#Optimize-Options

Особенно -fassociative-math, -ffast-math и -ffloat-store

Согласно руководству g ++, ононе изменит ваше выражение лица, если вы специально не запросите его.

5 голосов
/ 05 октября 2011

Это очень актуальная проблема, потому что компилятор Intel C ++, который очень широко используется, по умолчанию выполняет оптимизацию, которая может изменить результат.

См. http://software.intel.com/sites/products/documentation/hpc/compilerpro/en-us/cpp/lin/compiler_c/copts/common_options/option_fp_model.htm#option_fp_model

4 голосов
/ 05 октября 2011

Да, это безопасно (по крайней мере, в этом случае).Здесь вы используете только два «оператора»: первичное выражение (something) и двоичное something +/- something (добавка).

Раздел 1.9 Program execution (из C ++ 0x N3092):

Операторы могут быть перегруппированы в соответствии с обычными математическими правилами только тогда, когда операторы действительно являются ассоциативными или коммутативными.

В терминах группировки 5.1 Primary expressions заявляет:

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

Я считаю, что использованиеСлово «идентичный» в этой цитате требует соответствующей реализации, чтобы гарантировать, что оно будет выполнено в указанном порядке, если только другой заказ не может дать точных таких же результатов.

А для сложения и вычитания в разделе 5.7 Additive operators есть:

Аддитивные операторы + и - группа слева направо.

Таким образом, стандарт диктует results. Если компилятор может удостовериться, что одни и те же результаты могут быть получены при различном порядке операций, он может переупорядочить их.Но случится это или нет, вы не сможете заметить разницу.

2 голосов
/ 05 октября 2011

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

Но опасайтесь повышенной точности регистров FP .

Обратитесь к документации компилятора, чтобы убедиться, что значения FP не имеют повышенной точности.

0 голосов
/ 02 декабря 2015

Если вам действительно нужно, я думаю, вы можете сделать функцию noinline no_reorder (float x) {return x;}, а затем используйте его вместо скобок.Очевидно, что это не особенно эффективное решение.

0 голосов
/ 05 октября 2011

Краткий ответ: компилятор, вероятно, изменит порядок ваших вычислений, но он никогда не изменит поведение вашей программы (если ваш код не использует выражения с неопределенным поведением: http://blog.regehr.org/archives/213)

Однако вы все равно можете повлиять на это поведение, отключив все оптимизации компилятора (опция "-O0" с gcc). Если вам все еще нужен компилятор для оптимизации остальной части вашего кода, вы можете поместить эту функцию в отдельный ".c", которыйвы можете скомпилировать с "-O0". Кроме того, вы можете использовать некоторые хаки. Например, если вы чередуете свой код с вызовами функций extern, компилятор может счесть, что переупорядочивать ваш код небезопасно, поскольку функция может иметь неизвестную сторону-effect. Вызов «printf» для печати значения ваших промежуточных результатов приведет к аналогичному поведению.

В любом случае, если только у вас нет очень веской причины (например, отладка), вы обычно не хотите об этом заботиться, и вы должны доверять компилятору.

0 голосов
/ 05 октября 2011

Согласно другим ответам, вы должны быть в состоянии полагаться на то, что компилятор делает правильные вещи - большинство компиляторов позволяют компилировать и проверять ассемблер (используйте -S для gcc) - вы можете сделать это, чтобы сделатьуверен, что вы получили ожидаемый порядок операций.

Различные уровни оптимизации (в gcc, -O _O2 и т. д.) позволяют перестроить код (однако на такой последовательный код это вряд ли повлияет) - ноЯ бы посоветовал вам затем выделить эту конкретную часть кода в отдельный файл, чтобы вы могли контролировать уровень оптимизации только для расчета.

0 голосов
/ 05 октября 2011

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

ОДНАКО, также гарантируется, что это НИКОГДА не изменит результат.C ++ следует стандартному порядку операций, и все оптимизации сохраняют это поведение.

Итог: не беспокойтесь об этом.Напишите ваш код на C ++, чтобы он был математически правильным, и доверяйте компилятору.Если что-то пошло не так, проблема, скорее всего, была не в компиляторе.

0 голосов
/ 05 октября 2011

Да. Компилятор не изменит порядок ваших вычислений в таком блоке.

0 голосов
/ 05 октября 2011

В общем, вы должны быть в состоянии - оптимизатор должен знать свойства реальных операций.

Тем не менее, я бы проверил, черт возьми, компилятор, который я использовал.

...