gpt4 book ai didi

ARM NEON : Convert a binary 8-bit-per-pixel image (only 0/1) to 1-bit-per-pixel?

转载 作者:行者123 更新时间:2023-12-05 08:45:58 27 4
gpt4 key购买 nike

我正在执行一项任务,将每个像素有 8 位 (uint8_t) 且每个像素只能为 0 或 1(或 255)的大型二进制标签图像转换为数组uint64_t 数字和 uint64_t 数字中的每一位代表一个标签像素。

例如,

输入数组:0 1 1 0 ... (00000000 00000001 00000001 00000000 ...)

或输入数组:0 255 255 0 ... (00000000 11111111 11111111 00000000 ...)

输出数组(number):6(因为每个uint8_t转换成bit后,变成0110)

目前实现这个的C代码是:

 for (int j = 0; j < width >> 6; j++) {
uint8_t* in_ptr= in + (j << 6);
uint64_t out_bits = 0;
if (in_ptr[0]) out_bits |= 0x0000000000000001;
if (in_ptr[1]) out_bits |= 0x0000000000000002;
.
.
.
if (in_ptr[63]) out_bits |= 0x8000000000000000;
*output = obits; output ++;
}

ARM NEON 能否优化此功能?请帮忙。谢谢!

最佳答案

假设输入值为 0 或 255,下面是基本版本,非常简单,特别适合有 Intel SSE/AVX 经验的人。

void foo_basic(uint8_t *pDst, uint8_t *pSrc, intptr_t length)
{
//assert(length >= 64);
//assert(length & 7 == 0);
uint8x16_t in0, in1, in2, in3;
uint8x8_t out;
const uint8x16_t mask = {1, 2, 4, 8, 16, 32, 64, 128, 1, 2, 4, 8, 16, 32, 64, 128};

length -= 64;

do {
do {
in0 = vld1q_u8(pSrc); pSrc += 16;
in1 = vld1q_u8(pSrc); pSrc += 16;
in2 = vld1q_u8(pSrc); pSrc += 16;
in3 = vld1q_u8(pSrc); pSrc += 16;

in0 &= mask;
in1 &= mask;
in2 &= mask;
in3 &= mask;

in0 = vpaddq_u8(in0, in1);
in2 = vpaddq_u8(in2, in3);

in0 = vpaddq_u8(in0, in2);

out = vpadd_u8(vget_low_u8(in0), vget_high_u8(in0));

vst1_u8(pDst, out); pDst += 8;

length -= 64;
} while (length >=0);

pSrc += length>>3;
pDst += length;
} while (length > -64);
}

然而,Neon 具有非常用户友好和高效的排列和位操作指令,允许“垂直”

void foo_advanced(uint8_t *pDst, uint8_t *pSrc, intptr_t length)
{
//assert(length >= 128);
//assert(length & 7 == 0);
uint8x16x4_t in0, in1;
uint8x16x2_t row04, row15, row26, row37;

length -= 128;

do {
do {
in0 = vld4q_u8(pSrc); pSrc += 64;
in1 = vld4q_u8(pSrc); pSrc += 64;

row04 = vuzpq_u8(in0.val[0], in1.val[0]);
row15 = vuzpq_u8(in0.val[1], in1.val[1]);
row26 = vuzpq_u8(in0.val[2], in1.val[2]);
row37 = vuzpq_u8(in0.val[3], in1.val[3]);

row04.val[0] = vsliq_n_u8(row04.val[0], row15.val[0], 1);
row26.val[0] = vsliq_n_u8(row26.val[0], row37.val[0], 1);
row04.val[1] = vsliq_n_u8(row04.val[1], row15.val[1], 1);
row26.val[1] = vsliq_n_u8(row26.val[1], row37.val[1], 1);

row04.val[0] = vsliq_n_u8(row04.val[0], row26.val[0], 2);
row04.val[1] = vsliq_n_u8(row04.val[1], row26.val[1], 2);

row04.val[0] = vsliq_n_u8(row04.val[0], row04.val[1], 4);

vst1q_u8(pDst, row04.val[0]); pDst += 16;

length -= 128;
} while (length >=0);

pSrc += length>>3;
pDst += length;
} while (length > -128);
}

只有 Neon 的高级版本更短更快,但是 GCC 在处理 Neon 特定的排列指令如 vtrnvzip 时非常糟糕code> 和 vuzp

https://godbolt.org/z/bGdbohqKe

Clang 也好不到哪里去:它发送不必要的 vorr 垃圾邮件,其中 GCCvmov 执行相同的操作。

    .syntax unified
.arm
.arch armv7-a
.fpu neon
.global foo_asm
.text

.func
.balign 64
foo_asm:
sub r2, r2, #128

.balign 16
1:
vld4.8 {d16, d18, d20, d22}, [r1]!
vld4.8 {d17, d19, d21, d23}, [r1]!
vld4.8 {d24, d26, d28, d30}, [r1]!
vld4.8 {d25, d27, d29, d31}, [r1]!
subs r2, r2, #128

vuzp.8 q8, q12
vuzp.8 q9, q13
vuzp.8 q10, q14
vuzp.8 q11, q15

vsli.8 q8, q9, #1
vsli.8 q10, q11, #1
vsli.8 q12, q13, #1
vsli.8 q14, q15, #1

vsli.8 q8, q10, #2
vsli.8 q12, q14, #2

vsli.8 q8, q12, #4

vst1.8 {q8}, [r0]!
bpl 1b

add r1, r1, r2
cmp r2, #-128
add r0, r0, r2, asr #3

bgt 1b
.balign 8
bx lr

.endfunc
.end

最内层的循环包括:
GCC:32 条指令
Clang:30 条指令
汇编:18 条指令

不需要火箭科学就可以找出最快的速度和速度:如果您要进行排列,切勿相信编译器。

关于ARM NEON : Convert a binary 8-bit-per-pixel image (only 0/1) to 1-bit-per-pixel?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/70764147/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com