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 tegouchar4
. 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.).
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
'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