Подтвердить что ты не робот

Оптимизация с плавающей точкой - руководство

Большинство проблем, связанных с научными вычислениями, которые нам нужны, реализуя конкретный алгоритм в C/С++, требуют точности, которая намного ниже двойной точности. Например, точность 1e-6, 1e-7 охватывает 99% случаев для решателей ODE или численного интегрирования. Даже в редких случаях, когда нам нужна более высокая точность, обычно сам вычислительный метод терпит неудачу, прежде чем мы сможем достичь точности, близкой к двойной точности. Пример: мы не можем ожидать точности 1е-16 от простого метода Рунге-Кутты даже при решении стандартного обыкновенного дифференциального уравнения ностифа из-за ошибок округления. В этом случае требование двойной точности аналогично запросу, чтобы иметь лучшую аппроксимацию неправильного ответа.

Затем в большинстве случаев агрессивная оптимизация точек с плавающей точкой является беспроигрышной ситуацией, поскольку она делает ваш код быстрее (намного быстрее!) и не влияет на целевую точность вашей конкретной проблемы. Тем не менее, кажется, что очень сложно убедиться, что конкретная реализация/код устойчива к оптимизации fp. Классический (и несколько тревожный) пример: GSL, научная библиотека GNU, является не только стандартной числовой библиотекой на рынке, но и очень хорошо написанной библиотекой (я не могу себе представить, что я делаю лучшую работу). Однако GSL не устойчив к оптимизации fp. Фактически, если вы скомпилируете GSL с помощью компилятора Intel, то его внутренние тесты потерпят неудачу, если вы не включите флаг -fp-model strict, который отключит оптимизацию fp.

Таким образом, мой вопрос: существуют ли общие рекомендации для написания кода, который является устойчивым против агрессивных оптимизаций с плавающей запятой. Являются ли эти рекомендации языком (компилятором) конкретными. Если да, то каковы лучшие практики C/С++ (gcc/icc)?

Примечание 1: этот вопрос не задает вопрос о том, какие флаги оптимизации fp в gcc/icc.

Примечание 2: Этот вопрос не задает общих правил оптимизации C/С++ (например, не используйте виртуальные функции для небольших функций, которые называются много).

Примечание 3: Этот вопрос не запрашивает список большинства стандартных оптимизаций fp (например, x/x → 1).

Примечание 4: Я твердо верю, что это НЕ субъективный/вне темы вопрос, похожий на классический "The Coolest Server Names". Если вы не согласны (потому что я не предоставляю конкретный пример/код/​​проблему), пожалуйста, отметьте это как сообщество wiki. Я гораздо больше интересуюсь ответом, чем получаю несколько статусных очков (не они не важны - вы понимаете!).

4b9b3361

Ответ 1

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

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

Теперь ваш вопрос может быть следующим: "Как написать числовой код?". Здесь ваш вопрос может быть немного шире: есть целые книги, посвященные этому предмету. На странице Wikipedia, с которой я уже связан, есть хороший пример, а здесь - еще один хороший. Я не мог рекомендовать книгу в частности, это не моя область знаний.

Примечание 1: Желательная численная стабильность выходит за рамки оптимизации компилятора. Если у вас есть выбор, напишите числовой код, даже если вы не планируете использовать оптимизацию -ffast-math -style. Числовой неустойчивый код может давать неверные результаты даже при компиляции со строгой семантикой с плавающей запятой IEEE 754.

Примечание 2: вы не можете ожидать, что внешние библиотеки будут работать при компиляции с флагами -ffast-math -style. Эти библиотеки, написанные экспертами с плавающей запятой, возможно, должны будут играть тонкие трюки со свойствами вычислений IEEE 754. Этот трюк может быть нарушен оптимизациями -ffast-math, но они повышают производительность больше, чем вы могли ожидать от компилятора, даже если вы его допустили. Для вычислений с плавающей запятой эксперт по знаниям доменов каждый раз превосходит компилятор. Например, среди многих есть тройная двойная реализация, найденная в CRlibm. Этот код ломается, если он не скомпилирован со строгой семантикой IEEE 754. Другой, более простой алгоритм, который прерывает оптимизацию компилятора, суммирование Kahan: при компиляции с небезопасными оптимизациями c = (t - sum) - y оптимизирован до c = 0. Это, конечно, полностью разрушает цель алгоритма.