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

Избегание денормальных значений в С++

После долгого поиска ошибки производительности я прочитал о значениях денормальной с плавающей запятой.

Очевидно, что денормализованные значения с плавающей запятой могут быть серьезной проблемой с точки зрения производительности, как показано в этом вопросе: Почему изменение 0,1f в 0 замедляет производительность на 10x?

У меня есть Intel Core 2 Duo, и я компилирую с помощью gcc, используя -O2.

Так что мне делать? Могу ли я как-нибудь научить g++ избежать денормальных значений? Если нет, могу ли я как-то проверить, является ли float денормальным?

4b9b3361

Ответ 1

Вы можете проверить, является ли float денормальным, используя

#include <cmath>

if ( std::fpclassify( flt ) == FP_SUBNORMAL )

(Предостережение: я не уверен, что это будет выполняться на полной скорости на практике.)

В С++ 03, и этот код работал у меня на практике,

#include <cmath>
#include <limits>

if ( flt != 0 && std::fabsf( flt ) < std::numeric_limits<float>::min() ) {
    // it denormalized
}

Чтобы решить, где применить это, вы можете использовать анализатор на основе образца, такой как Shark, VTune или Zoom, чтобы подчеркнуть, что инструкции замедлились по денормальным значениям. Микро-оптимизация, даже больше, чем другие оптимизации, абсолютно безнадежна без анализа как до, так и после.

Ответ 2

Подождите. Прежде чем что-либо делать, знаете ли вы, что ваш код сталкивается с денормальными значениями и что они оказывают заметное влияние на производительность?

Предполагая, что вы знаете это, знаете ли вы, что алгоритм (ы), который вы используете, является стабильным, если поддержка denormal отключена? Получение неправильного ответа в 10 раз быстрее обычно не является хорошей оптимизацией производительности.

Эти проблемы в стороне:

  • Если вы хотите обнаружить денормальные значения, чтобы подтвердить их присутствие, у вас есть несколько вариантов. Если у вас есть стандартная библиотека C99 или Boost, вы можете использовать макрос fpclassify. Кроме того, вы можете сравнить абсолютные значения ваших данных с наименьшим положительным нормальным числом.

  • Вы можете установить аппаратное обеспечение для сброса значений денормальности до нуля (FTZ) или обработать денормальные входы как ноль (DAZ). Самый простой способ, если он должным образом поддерживается на вашей платформе, вероятно, использовать функцию fesetenv( ) в заголовке C fenv.h. Тем не менее, это одна из наименее широко поддерживаемых функций стандарта C и по своей сути является платформой. Вы можете просто использовать некоторую встроенную сборку, чтобы напрямую установить состояние FPU (DAZ/FTZ).

Ответ 3

Большинство математических сопроцессоров имеют возможность обрезать денормальные значения до нуля. На x86 это флаг FZ (Flush to Zero) в регистре управления MXCSR. Проверьте реализацию CRT для функции поддержки, чтобы установить регистр управления. Он должен быть в <float.h>, что-то похожее на _controlfp(). Бит опции обычно имеет "FLUSH" в #defined символе.

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

Ответ 4

Чтобы иметь (с нуля до нуля) FTZ (при условии, что underflow по умолчанию замаскирован) в gcc:

#define CSR_FLUSH_TO_ZERO         (1 << 15)
unsigned csr = __builtin_ia32_stmxcsr();
csr |= CSR_FLUSH_TO_ZERO;
__builtin_ia32_ldmxcsr(csr);

Если это не очевидно из имен, __builtin_ia32_stmxcsr и __builtin_ia32_ldmxcsr доступны, только если вы нацеливаете процессор x86. ARM, Sparc, MIPS и т.д. Каждый будет нуждаться в отдельном коде для конкретной платформы с этим подходом.

Ответ 5

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

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

Ответ 6

Вам, по-видимому, нужны некоторые команды процессора, называемые FTZ (Flush To Zero) и DAZ (Denormals Are Zero).

Я нашел информацию на аудио-сайте, но их ссылка на документацию Intel отсутствовала. Они, по-видимому, являются инструкциями SSE2, поэтому они должны работать на процессорах AMD, которые это поддерживают.

Я не знаю, что вы можете сделать в GCC, чтобы заставить это переноситься. Вы всегда можете написать встроенный ассемблерный код, чтобы использовать их. Возможно, вам придется заставить GCC использовать только SSE2 для математики с плавающей запятой.