我正在寻找一种将 8 位源数组 (
uint8_t*
) 中的元素加载到 AArch64 NEON / ASIMD 寄存器的方法,数据格式为 uint16x8_t
甚至更好 uint16x8x3_t
。所以基本上,源数组中的每个字节都必须作为短字节加载到寄存器中。
在 for 循环中,我必须在每次迭代中使用一批新值进行加载。
我找不到任何 ASIMD 内在函数来执行此操作,但也许我错过了一些东西。我当前的方法是首先将元素加载为
uint8x8x3_t
,执行加宽左移(使用vmovl_u8
,使元素变成uint16x8_t
)但这似乎非常低效:
uint8x8x3_t bgrChunk = vld3_u8(bgr);
uint16x8_t b = vmovl_u8(bgrChunk.val[0]);
uint16x8_t g = vmovl_u8(bgrChunk.val[1]);
uint16x8_t r = vmovl_u8(bgrChunk.val[2]);
bgr += 24; // Required for next iteration
我也尝试过以下方法,但这比上面的表现更差;
uint16_t bgrValues[] = { bgr++, bgr++, bgr++, ... repeat up to 24 elements ..., bgr++, bgr++ };
uint16x8x3_t bgrChunk = vld3q_u16(bgrValues);
是否有更有效的方法来做到这一点,或者我是否缺少一些内在的东西,这将使我更容易做到这一点?
编辑;我想要的扩展示例
假设我有一个数组
uint8_t*
,其值为 { 5, 33, 102, 153... }
有没有一种方法可以将每个 8 位individual 元素作为 16 位值直接加载到寄存器中,以便该寄存器将包含 16-bit 值 { 5, 33, 102, 153.. . }?
void foo(uint8_t* bgr, uint16_t width, uint16_t height) {
for (uint16_t y = 0; y < height; y++) {
for (uint16_t x = 0; x < width; x += 8) {
// I want to load 8-bit values as 16-bit values here. Is there a more efficient way to do this than the code below?
uint8x8x3_t bgrChunk = vld3_u8(bgr);
uint16x8_t b = vmovl_u8(bgrChunk.val[0]);
uint16x8_t g = vmovl_u8(bgrChunk.val[1]);
uint16x8_t r = vmovl_u8(bgrChunk.val[2]);
bgr += 24;
// ... Some operations working on the loaded data
}
}
}
它是一个正交指令集,因此为了扩大负载,您需要将 8 位值加载到寄存器中,然后作为第二次操作扩大到 16 位。
根据您接下来要做什么,第二个操作通常可能是一个有用的算术运算,而不仅仅是一次移动。例如,
vmull_s8()
、vaddl_s8()
、vsubl_s8()
都返回 16 位结果。如果您想走另一条路,也有类似的缩小等价物。