2012-01-29 69 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