2012-01-29 11 views
7

Mam problem z metodą SSE, którą piszę, która wykonuje przetwarzanie dźwięku. I wdrożyliśmy SSE losową funkcję opartą na papierze Intela tutaj:Wewnętrzne elementy SSE powodują, że normalna operacja pływająca zwraca -1. # INV

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

Mam też sposób, który przeprowadza konwersje float na S16 użyciu SSE też, konwersja odbywa się po prostu w następujący sposób:

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 jest zdefiniowany w następujący sposób:

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

Jeśli sseresult != NULL__m128 wynik jest zwracany, a result nie jest używany. To działa idealnie na pierwszej pętli, ale w następnej pętli delta staje się -1.#INF zamiast 1.0. Jeśli skomentuję linię __m64 con = _mm_cvtps_pi16(in);, problem zniknie.

Myślę, że FPU wchodzi w nieznany stan czy coś.

+0

_mm_cvtps_pi16 to zły pomysł. Użyj kombinacji _mm_cvtps_epi32, _mm_packs_epi32 i _mm_store_si128/_mm_storeu_si128, aby przekonwertować 8 elementów pływających na 8 int16_t i Twój problem zniknął! –

Odpowiedz

9

Mieszanie SSE arytmetyki liczb całkowitych i (regularny) zmiennoprzecinkowej matematyki. Może produkować dziwne wyniki, ponieważ oba działają w tych samych rejestrach. Jeśli używasz:

_mm_empty() 

FPU jest resetowany do prawidłowego stanu. Microsoft ma Guidelines for When to Use EMMS

+0

Dokładnie problem, dzięki! – Geoffrey

+1

nie jest tak tylko z powodu _mm_cvtps_pi16? Myślałem, że _mm_empty to tylko MMX. Zastąpiłbym to, ponieważ _mm_empty jest kosztownym AFAIK. – Sam

+0

Tak, tym bardziej poprawnym rozwiązaniem jest zlikwidowanie instrukcji FPU i trzymanie się SSE aż do ukończenia, ale była to poprawna odpowiedź, ponieważ wyjaśniła, dlaczego tak się stało. – Geoffrey

1
  • _mm_load_ps nie ma gwarancji wyrównania obciążenia. dane float * mogą być wyrównane do 4 bajtów zamiast 16 _ => _mm_loadu_ps
  • memcpy prawdopodobnie zabije zalety osiągnięte dzięki SSE, powinieneś użyć polecenia store dla __m64, ale tutaj znowu zajmij się wyrównaniem. Jeśli nie można wykonać niezalecanego strumienia lub magazynu __m64, powinienem zachować go wewnątrz _m128i i zrobić zamaskowany zapis za pomocą _mm_maskmoveu_si128 lub zapisać te 8 bajtów ręcznie.

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

+0

Dzięki za wskazówki, powinienem był stwierdzić, że kod wyrównania jest pominięty w wysłanej próbce, wszystkie dane przekazane do tej metody są wyrównane. – Geoffrey

+0

Jak przechowywać ręcznie 8 bajtów? – Geoffrey

+1

Myślałem o związku z tablicą uint8_t [8], aby skopiować ręcznie. Ale zawsze istnieje problem, że takie konstrukcje (i memcpy) mogą powodować "ładowanie magazynu". Dlatego przeniesienie __int64 (lub dwóch z nich) do rejestru 128-bitowego i wykonanie odpowiednio _mm_maskmoveu_si128 lub _mm_stream * powinno być bardziej wydajne. Strumieniowanie zapobiega zanieczyszczaniu pamięci podręcznej danymi wyjściowymi, które mogą być interesujące, ponieważ raz napisane, nie potrzebujesz ich od razu. – Sam