Как избежать денормальных значений в C ++ - PullRequest
24 голосов
/ 21 марта 2010

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

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

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

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

Ответы [ 6 ]

23 голосов
/ 21 марта 2010

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

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

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

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

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

14 голосов
/ 21 марта 2010

Вы можете проверить, является ли число с плавающей запятой ненормальным, используя

#include <cmath>

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

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

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

#include <cmath>
#include <limits>

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

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

7 голосов
/ 21 марта 2010

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

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

4 голосов
/ 21 ноября 2011

Иметь (сбрасывать в ноль) FTZ (при условии, что недополнение маскируется по умолчанию) в 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 и т. Д. Каждый будет нуждаться в отдельном коде для конкретной платформы с этим подходом.

1 голос
/ 21 марта 2010

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

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

0 голосов
/ 21 марта 2010

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

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

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

...