2012-01-29 7 views
7

Ho riscontrato un problema con un metodo SSE che sto scrivendo che esegue l'elaborazione audio. Ho implementato una funzione casuale SSE basato su carta Intel qui:Gli intrinseci SSE causano il normale funzionamento float per restituire -1. # INV

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

Ho anche un metodo che esegue conversioni da Mobile a S16 utilizzando SSE inoltre, la conversione viene eseguita semplicemente come segue:

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 è definito come segue:

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; 
    ... 
} 

Se sseresult != NULL il 01.236.142,623251 millions Il risultatoviene restituito e result non viene utilizzato. Ciò si esegue perfettamente sul primo ciclo, ma sul ciclo successivo delta diventa -1.#INF anziché 1.0. Se commento la riga __m64 con = _mm_cvtps_pi16(in); il problema scompare.

Penso che la FPU stia entrando in uno stato sconosciuto o qualcosa del genere.

+0

_mm_cvtps_pi16 è una cattiva idea. Usa una combinazione di _mm_cvtps_epi32, _mm_packs_epi32 e _mm_store_si128/_mm_storeu_si128 per convertire 8 float in 8 int16_t e il tuo problema è scomparso! –

risposta

9

miscelazione SSE aritmetica intera e (regolare) calcoli in virgola mobile. Può produrre risultati strani perché entrambi funzionano sugli stessi registri. Se si utilizza:

_mm_empty() 

, la FPU viene ripristinata in uno stato corretto. Microsoft ha Guidelines for When to Use EMMS

+0

Esattamente il problema, grazie! – Geoffrey

+1

non è solo per _mm_cvtps_pi16? Ho pensato che _mm_empty è solo MMX. Quindi lo sostituirò, poiché _mm_empty è costoso AFAIK. – Sam

+0

Sì, la soluzione più corretta è eliminare quelle istruzioni della FPU e attenersi a SSE fino al completamento, ma questa era la risposta corretta in quanto spiegava perché si stava verificando. – Geoffrey

1
  • _mm_load_ps non è garantito per eseguire un carico allineato. float * i dati possono essere allineati a 4 byte invece di 16 _ => _mm_loadu_ps
  • memcpy probabilmente ucciderà i vantaggi ottenuti con SSE, dovresti usare un comando store per __m64 ma anche qui, prenditi cura dell'allineamento. Se è impossibile eseguire un flusso o un archivio non allineato di __m64, lo terrei all'interno di _m128i e farei una scrittura mascherata con _mm_maskmoveu_si128 o memorizzerò questi 8 byte a mano.

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

+0

Grazie per i suggerimenti, avrei dovuto dichiarare che il codice di allineamento è stato omesso dall'esempio pubblicato, tutti i dati passati a questo metodo sono allineati. – Geoffrey

+0

Come memorizzeresti 8 byte a mano? – Geoffrey

+1

Ho pensato a un unione con un array uint8_t [8] da copiare manualmente. Ma c'è sempre il problema, che tali costrutti (e memcpy) possono causare "store to load". Quindi trasferire il __int64 (o due di essi) in un registro a 128 bit e fare _mm_maskmoveu_si128 o _mm_stream * rispettivamente dovrebbe essere più efficiente. Lo streaming evita l'inquinamento della cache con l'output, che può essere di interesse, dal momento che una volta scritto, non ne hai più bisogno immediatamente. – Sam

Problemi correlati