2012-01-29 3 views
7

У меня возникла проблема с методом SSE, который я пишу, который выполняет обработку звука. Я выполнил случайную функцию SSE, основанную на работе компании Intel здесь:SSE intrinsics вызывает нормальную операцию float для возврата -1. # INV

http://software.intel.com/en-us/articles/fast-random-number-generator-on-the-intel-pentiumr-4-processor/

У меня также есть метод, который выполняет преобразование из Float в S16 с использованием SSE также, преобразование выполняется достаточно просто следующим образом:

unsigned int Float_S16LE(float *data, const unsigned int samples, uint8_t *dest) 
{ 
    int16_t *dst = (int16_t*)dest; 
    const __m128 mul = _mm_set_ps1((float)INT16_MAX); 
    __m128 rand; 
    const uint32_t even = count & ~0x3; 
    for(uint32_t i = 0; i < even; i += 4, data += 4, dst += 4) 
    { 
    /* random round to dither */ 
    FloatRand4(-0.5f, 0.5f, NULL, &rand); 

    __m128 rmul = _mm_add_ps(mul, rand); 
    __m128 in = _mm_mul_ps(_mm_load_ps(data),rmul); 
    __m64 con = _mm_cvtps_pi16(in); 

    memcpy(dst, &con, sizeof(int16_t) * 4); 
    } 
} 

FloatRand4 определяется следующим образом:

static inline void FloatRand4(const float min, const float max, float result[4], __m128 *sseresult = NULL) 
{ 
    const float delta = (max - min)/2.0f; 
    const float factor = delta/(float)INT32_MAX; 
    ... 
} 

Если sseresult != NULL__m128 результат возвращается и result не использовался. Это отлично работает в первом цикле, но в следующем цикле delta становится -1.#INF вместо 1.0. Если я прокомментирую строку __m64 con = _mm_cvtps_pi16(in);, проблема исчезнет.

Я думаю, что FPU попадает в неизвестное состояние или что-то в этом роде.

+0

_mm_cvtps_pi16 - плохая идея. Используйте комбинацию _mm_cvtps_epi32, _mm_packs_epi32 и _mm_store_si128/_mm_storeu_si128, чтобы преобразовать 8 поплавков в 8 int16_t, и ваша проблема исчезла! –

ответ

9

Смешивание SSE целочисленной арифметики и (обычная) с плавающей точкой математике. Может производить странные результаты, потому что оба работают на одних и тех же регистрах. Если вы используете:

_mm_empty() 

FPU сбрасывается в правильное состояние. Microsoft имеет Guidelines for When to Use EMMS

+0

Точно проблема, спасибо! – Geoffrey

+1

разве это не из-за _mm_cvtps_pi16? Я думал, что _mm_empty - только MMX. Поэтому я бы заменил это, поскольку _mm_empty является дорогостоящим AFAIK. – Sam

+0

Да, более правильным решением является устранение этих инструкций FPU и привязка к SSE до завершения, но это был правильный ответ, поскольку он объяснял, почему это происходит. – Geoffrey

1
  • _mm_load_ps не гарантирует выравнивание нагрузки. float * данные могут быть выровнены с 4 байтами вместо 16 _ => _mm_loadu_ps
  • memcpy, вероятно, убьет преимущества, достигнутые с помощью SSE, вы должны использовать команду store для __m64, но и здесь, позаботьтесь о выравнивании. Если невозможно выполнить нисходящий поток или хранилище __m64, я бы либо сохранил его внутри _m128i, и сделал маскаратную запись с помощью _mm_maskmoveu_si128 или сохранил эти 8 байтов вручную.

http://msdn.microsoft.com/en-us/library/bytwczae.aspx

+0

Спасибо за советы, я должен был указать, что код выравнивания опущен из отправленного образца, все данные, переданные этому методу, выровнены. – Geoffrey

+0

Как вы храните 8 байтов вручную? – Geoffrey

+1

Я думал о объединении с массивом uint8_t [8] для копирования вручную. Но всегда есть проблема, что такие конструкции (и memcpy) могут вызвать загрузку хранилища. Поэтому передача __int64 (или двух из них) в 128-битный регистр и выполнение _mm_maskmoveu_si128 или _mm_stream * соответственно должны быть более эффективными. Потоковая передача позволяет избежать загрязнения кэша с помощью вывода, который может представлять интерес, поскольку с момента его написания вам не нужна его снова сразу. – Sam

Смежные вопросы