2013-06-16 10 views
5

W OpenCL, chcę zapisać wektor (3D) za pomocą reprezentacji "Współdzielonego wykładnika" dla pamięci kompaktowej. Zazwyczaj, jeśli przechowujesz wektor zmiennoprzecinkowy 3D, po prostu przechowujesz 3 oddzielne wartości zmiennoprzecinkowe (lub 4, gdy są odpowiednio wyrównane). Wymaga to 12 (16) bajtów pamięci dla pojedynczej precyzji, a jeśli nie potrzebujesz tej dokładności, możesz użyć "half" precision float i zmniejszyć ją do 6 (8) bajtów.Przedstawienie wektora zmiennoprzecinkowego "Współdzielony wykładnik" w OpenCL C

Przy użyciu pół precyzji i 3 odrębne wartości pamięć wygląda następująco (rozważali wyrównanie):

  • współrzędnej x: 1 bit znaku, 5 bitów wykładnik 10 bitów mantysę
  • współrzędnej y: 1 bit znaku, 5 bitów wykładnik 10 bitów mantysę
  • współrzędna: 1 bit znaku, 5 bitów wykładnik 10 bitów mantysę

ja jak kurczyć to do 4 bajtów, przy użyciu udostępniony wykładnik, ponieważ OpenGL używa tego w jednym ze swoich wewnętrznych formatów tekstur ("RGB9_E5"). Oznacza to, że absolutnie największy składnik decyduje o wykładniku liczby całkowitej. Ten wykładnik jest następnie używany niejawnie dla każdego komponentu. Sztuczki takie jak "znormalizowany" magazyn z niejawnym "1." przed mantysą nie działają w tym przypadku. Taka reprezentacja działa następująco (możemy dostosować parametry acutal, to jest to przykład):

  • współrzędnej x: 1 bit znaku, 8 bitów mantysę
  • współrzędnej y: 1 bit znaku, 8 bitów Mantysa
  • współrzędna: 1 bit znaku, 8 bitów mantysy
  • 5 bitów współdzielonych wykładnikowych

chciałabym zapisać to w OpenCL uint typu (32 bitów) lub coś równoważnego (np uchar4). Pytanie brzmi teraz:

Jak najszybciej i jak najszybciej przekonwertować z tej reprezentacji na i z float3?

Mój pomysł jest podobny do tego, ale jestem pewien, że istnieje jakiś „nieco hacking” trick, który wykorzystuje reprezentacja nieco IEEE pływaków do obejścia zmiennoprzecinkowych ALU:

  • Zastosowanie uchar4 jako przedstawiciel rodzaj. Przechowuj x, y, z mantisssa w elementach x, y, z tego uchar4. Składnik w jest podzielony na 5 mniej znaczących bitów (w & 0x1F) dla współdzielonego wykładnika, a trzy kolejne znaczące bity są znakami odpowiednio dla x, yi z.
  • Należy zauważyć, że wykładnik jest "obciążony" przez 16, tzn. Zapisana wartość 16 oznacza, że ​​reprezentowane liczby wynoszą do (nie wliczając) 1,0, a zapamiętana wartość 19 oznacza wartości do (nie wliczając) 8.0, a więc na.
  • "rozpakowanie" tej reprezentacji do float3 można zrobić przy użyciu tego kodu:

    float3 unpackCompactVector(uchar4 packed) { 
        float exp = (float)(packed.w & 0x1F) - 16.0; 
        float factor = exp2(exp)/256.0; 
        float x = (float)(packed.x) * factor * (packed.w & 0x20 ? -1.0 : 1.0); 
        float y = (float)(packed.y) * factor * (packed.w & 0x40 ? -1.0 : 1.0); 
        float z = (float)(packed.z) * factor * (packed.w & 0x80 ? -1.0 : 1.0); 
        float3 result = { x, y, z }; 
        return result; 
    } 
    
  • "Pakowanie" a float3 w tej reprezentacji mogą być wykonane przy użyciu tego kodu:

    uchar4 packCompactVector(float3 vec) { 
        float xAbs = abs(vec.x); uchar xSign = vec.x < 0.0 ? 0x20 : 0; 
        float yAbs = abs(vec.y); uchar ySign = vec.y < 0.0 ? 0x40 : 0; 
        float zAbs = abs(vec.z); uchar zSign = vec.z < 0.0 ? 0x80 : 0; 
        float maxAbs = max(max(xAbs, yAbs), zAbs); 
        int exp = floor(log2(maxAbs)) + 1; 
        float factor = exp2(exp); 
        uchar xMant = floor(xAbs/factor * 256); 
        uchar yMant = floor(yAbs/factor * 256); 
        uchar zMant = floor(zAbs/factor * 256); 
        uchar w = ((exp + 16) & 0x1F) + xSign + ySign + zSign; 
        uchar4 result = { xMant, yMant, zMant, w }; 
        return result; 
    } 
    

Wstawiłem równoważną implementację w C++ online on ideone. Przypadki testowe pokazują przejście z exp = 3 do exp 4 (z odchyleniem 16, które jest zakodowane odpowiednio jako 19 i 20) poprzez kodowanie liczb około 8.0.

Ta implementacja wydaje się działać na pierwszy rzut oka. Ale:

  • Istnieje kilka przypadków narożnych, których nie obejmowałem, w szczególności przekroczenia i niedomiaru (wykładnika).
  • Nie chcę używać funkcji matematycznych zmiennoprzecinkowych, takich jak log2, ponieważ są one wolne.

Czy możesz zaproponować lepszy sposób osiągnięcia mojego celu?

pamiętać, że tylko potrzebujemy OpenCL „urządzenia kod” za to, że nie trzeba konwertować między reprezentacjami w gospodarza programu. Dodałem jednak znacznik C, ponieważ rozwiązanie jest najprawdopodobniej niezależne od funkcji językowych OpenCL (OpenCL jest prawie C, a także wykorzystuje ruchy IEEE 754, manipulowanie bitami działa tak samo, itp.).

+1

Właśnie znalazłem [tę dokumentację] (http://developer.download.nvidia.com/opengl/specs/GL_EXT_texture_shared_exponent.txt), który ma kod C (zaczynający się w środku dokumentu), który wydaje się obiecujący .. – leemes

+0

'Podłoga (log2())' może być zastąpiona przez bitową operację dzielenia i arytmetyki liczb całkowitych w celu wyodrębnienia i ponownego rozmiaru/ponownego odchylenia wykładnika 'maxAbs' bez konieczności obliczania części ułamkowej logarytmu. . Nie ma tu zastosowania, ale kiedy masz liczbę całkowitą, możesz również użyć 'clz' (zliczanie zer wiodących), które często będzie pojedynczą instrukcją maszyny. – user57368

Odpowiedz

0

Jeśli używałeś CL/GL interop i zapisałeś swoje dane w strukturze OpenGL w formacie RGB9_E5 i gdybyś mógł stworzyć obraz OpenCL z tej tekstury, możesz wykorzystać jednostkę tekstur sprzętowych do konwersji w float4 podczas czytania z obrazu. Może warto spróbować.

+0

Nie, tak nie jest. Jest częścią większej struktury umieszczam w buforze. Ale myślę, że odrzucam pomysł i używam trzech półpłynnych. Dzięki i tak;) – leemes