在OpenCL中,我想使用“共享指数”表示存储矢量(3D)以进行紧凑存储。通常,如果存储3D浮点矢量,则只需存储3个单独的浮点值(或正确对齐时为4)。这需要12(16)字节存储以实现单精度,如果您不需要此精度,则可以使用"half" precision float并将其缩小到6(8)字节。
当使用半精度和3个单独的值时,内存看起来像这样(不考虑对齐):
我想通过使用共享指数将其缩小到4个字节,因为OpenGL在其内部纹理格式之一(“RGB9_E5”)中使用它。这意味着,绝对最大的组件决定了整数的指数。然后隐式地将该指数用于每个组件。诸如“规范化”存储之类的技巧具有隐含的“1”。在这种情况下,在尾数前面不起作用。这样的表示就像这样(我们可以调整实际参数,所以这是一个例子):
我想将它存储在OpenCL uint
类型(32位)或类似的东西(例如uchar4
)中。现在的问题是:
如何尽可能快地从float3
转换为此表示形式?
我的想法是这样的,但我确信有一些“有点黑客”技巧,它使用IEEE浮点数的位表示来规避浮点ALU:
uchar4
作为代表类型。将x,y,z mantisssa存储在此uchar4
的x,y,z分量中。对于共享指数,w分量被分成5个较低有效位(w & 0x1F)
,而另外三个有效位(w >> 5) & 1
,(w >> 6) & 1
和(w >> 7) & 1
是x,y的符号和z,分别。可以使用以下代码将此表示“解包”到float3
中:
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;
}
可以使用以下代码将float3
“打包”到此表示中:
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;
}
我在C ++中online on ideone放了一个等价的实现。测试用例通过编码exp = 3
周围的数字显示从exp 4
到8.0
的转换(偏差为16,分别编码为19和20)。
这种实现似乎一见钟情。但是:
log2
这样的浮点数学函数,因为它们很慢。你能建议一个更好的方法来实现我的目标吗?
请注意,我只需要一个OpenCL“设备代码”,我不需要在主机程序中的表示之间进行转换。但我添加了C
标签,因为解决方案很可能独立于OpenCL语言功能(OpenCL几乎是C,它也使用IEEE 754浮点数,位操作也一样,等等。)
答案 0 :(得分:0)
如果您使用CL / GL互操作并将数据存储在RGB9_E5格式的OpenGL纹理中,并且如果您可以从该纹理创建OpenCL图像,则可以利用硬件纹理单元在读取时转换为float4图片。可能值得尝试。