2012-01-29 52 views
7

我有一個SSE方法我寫執行音頻處理的問題。我已經實現了基於Intel的紙這裏SSE隨機函數:SSE內在引起正常浮動操作返回-1。#INV

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

我也有一個使用SSE還執行從浮點轉換到S16的方法,轉換很簡單如下進行:

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,並且問題消失了! – 2012-01-30 05:08:28

回答

9

混合SSE整數運算和(定期)浮點運算。可能會產生奇怪的結果,因爲它們都在相同的寄存器上運行。如果使用:

_mm_empty() 

FPU被重置爲正確的狀態。微軟Guidelines for When to Use EMMS

+0

問題的關鍵,謝謝! – Geoffrey 2012-01-29 10:59:05

+1

是不是隻是因爲_mm_cvtps_pi16?我以爲_mm_empty只是MMX。所以我會替換這個,因爲_mm_empty是昂貴的AFAIK。 – Sam 2012-01-29 11:10:36

+0

是的,更正確的解決方案是取消那些FPU指令並堅持SSE直到完成,但這是正確的答案,因爲它解釋了爲什麼它正在發生。 – Geoffrey 2012-01-29 11:44:11

1
  • _mm_load_ps不能保證做一個排列的負荷。浮筒*數據可以排列爲4個字節,而不是16 _ => _mm_loadu_ps
  • 的memcpy可能會殺死SSE取得優勢,你應該再次使用存儲命令爲__m64但在這裏,取排列的照顧。 如果不可能做一個未對齊的流或存儲一個__m64,我會保留它在一個_m128i中,並用_mm_maskmoveu_si128做一個掩碼寫或手動存儲這8個字節。

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

+0

感謝您的提示,我應該聲明從發佈的示例中省略了對齊代碼,傳遞給此方法的所有數據都是對齊的。 – Geoffrey 2012-01-29 10:58:46

+0

如何手動存儲8個字節? – Geoffrey 2012-01-29 11:12:44

+1

我想過用uint8_t數組[8]手動複製的聯合。但總是存在這樣的問題,即這樣的構造(和memcpy)會導致「存儲加載」。因此,將__int64(或其中兩個)分別轉換爲128位寄存器並分別執行_mm_maskmoveu_si128或_mm_stream *應該更高效。流式傳輸避免了輸出緩存污染,這可能是有趣的,因爲一旦寫入,您就不需要立即再次使用它。 – Sam 2012-01-29 11:29:18