在OpenCL中,代码就是这样编写的
void unpack_8bit_to_16bit( const __m128i a, __m128i& b0, __m128i& b1 )
{
__m128i zero = _mm_setzero_si128();
b0 = _mm_unpacklo_epi8( a, zero );
b1 = _mm_unpackhi_epi8( a, zero );
}
现在我想将此代码转换为c语言,是否可能?
答案 0 :(得分:2)
如评论中所述,这不是OpenCL代码。但是,如果您的意思是如何将此代码转换为 OpenCL,那么向量化的方法是使用向量类型,例如float4(四个32位浮点数),double3(三个64位双精度数) ,long8(8个64位整数)等...甚至还有内置的四核(128位浮点),复杂双打等硬核类型......
在你的情况下,你真正想要的是将一堆字节打包成16位字,分离输入的低和高四边形。您可以通过调配或显式计算每个向量来实现此目的,但是还有另一种方法可以执行此特定计算 - OpenCL有一个向量分割机制,它将任意向量类型拆分为两个较低和较高的一半。这是这样做的:
float4 input = (float4)(4.3, 0.71, 9.1, 44.8);
float2 inputLo = input.lo; // = (4.3, 0.71)
float2 inputHi = input.hi; // = (9.1, 44.8)
显然这可以解决你的问题,因为所有你需要做的就是将你的char16(16个8位字节)分成两个更低和更高的char8,并将这些char8解释为short8(因为你正在解包),要么通过强制转换或明确转换。
请注意,对于OpenCL来说这是一个奇怪的问题 - 这种解包机制源于数据必须打包到SSE寄存器的方式,所以如果你想从8位元素切换到16,你必须不断地改变字节。位。在OpenCL中,这是不必要的,因为您有不假定特定数据排列的向量类型(并且您可以轻松地从一种类型转换为另一种类型)。如果您的OpenCL内核恰好在支持SSE的处理器上执行,那么内核编译器将自动为您执行打包和解包 - 如果您的代码是理智的话,希望最佳。
你不能在OpenCL中使用内在函数,因为内核不能在x86和x64硬件上独占运行 - 它们也可以运行在GPU,FPGA和定制芯片上。因此,您使用通用向量类型,这些类型会自动转换为编译内核的平台上的正确SIMD指令(实际上,它有点复杂,但这是它的要点)。
鉴于您的最新评论,我将补充一点:如果您希望将内在函数转换为简单的C代码,则需要了解数据如何打包到SSE寄存器中。这就是它的工作原理,基本术语:每个SSE寄存器都是128位宽,因此可以容纳16个字节,8个字,4个长整数等等......你不能混用这些类型,所以你不能拥有例如2个字节和7个字,每个内部假定一个特定的类型(例如,你可能想要寄存器中每个64位双精度的平方根,或者每个32位浮点数的平方根!显然你选择哪种类型)
这些类型总是连续的,所以你想要将一个8字的向量转换成两个4长的向量,即“解包”它以便能够对它进行32位计算,这意味着你想要去从:
[16-bit][16-bit][16-bit][16-bit][16-bit][16-bit][16-bit][16-bit]
到
[32-bit][32-bit][32-bit][32-bit] & [32-bit][32-bit][32-bit][32-bit]
显然,您不能只重用该寄存器,因为两个16位字将合并为一个32位值,这将产生垃圾。相反,你必须有条不紊地将每个16位字拉出,将其转换为32位长,并将其放入新寄存器中 - SSE在硬件中完成所有这些操作(内部调用适当的指令)。
在您的特定情况下,您有一个包含16个字节的寄存器,并且您希望在另外两个寄存器中“输出”数据,而这些寄存器将包含8个字。因此,如果您的输入寄存器包含a0..a15(那些是字节),那么您将拥有:
b0 = (word)a_0..(word)a_7
b1 = (word)a_8..(word)a_15
您可以在C中使用数组执行此操作,并“模拟”SSE寄存器(您可以使用包含适合寄存器的每个可能向量的联合,或者只是硬编码不同的数组类型并相互转换)。
作为参考,请参阅this,它解释了一下(我还建议您阅读SSE寄存器的工作原理,因为这是打包存在的原因及其重要性。)