使用 Neon 加速 memcpy 和 memset
1 简介
在现代计算机系统中,内存操作是常见且至关重要的任务。对于需要频繁进行数据传输和初始化的应用场景,memcpy和memset函数的性能显得尤为重要。Neon是ARM公司推出的一种SIMD(单指令多数据)指令集,能够大幅提升内存操作的效率。本文将深入探讨如何使用Neon指令集优化memcpy和memset函数的性能,以实现更快的内存操作。
2 代码实现
2.1 实现 memcpy 操作
这里参考了 nihui 在 opencv-mobile 中对 RKMPP 数据拷贝时做的优化
inline void LockzhinerMemoryCopy(void* dst, const void* src, size_t size) {
#if __ARM_NEON
unsigned char* neon_dst = (unsigned char*)dst;
const unsigned char* neon_src = (const unsigned char*)src;
int nn = size / 64;
size -= nn * 64;
while (nn--) {
__builtin_prefetch(neon_src + 64);
uint8x16_t _p0 = vld1q_u8(neon_src);
uint8x16_t _p1 = vld1q_u8(neon_src + 16);
uint8x16_t _p2 = vld1q_u8(neon_src + 32);
uint8x16_t _p3 = vld1q_u8(neon_src + 48);
vst1q_u8(neon_dst, _p0);
vst1q_u8(neon_dst + 16, _p1);
vst1q_u8(neon_dst + 32, _p2);
vst1q_u8(neon_dst + 48, _p3);
neon_src += 64;
neon_dst += 64;
}
if (size > 16) {
uint8x16_t _p0 = vld1q_u8(neon_src);
vst1q_u8(neon_dst, _p0);
neon_src += 16;
neon_dst += 16;
size -= 16;
}
if (size > 8) {
uint8x8_t _p0 = vld1_u8(neon_src);
vst1_u8(neon_dst, _p0);
neon_src += 8;
neon_dst += 8;
size -= 8;
}
while (size--) {
*neon_dst++ = *neon_src++;
}
#else
memcpy(dst, src, size);
#endif
}
2.2 实现 memset 操作
参考 memcpy 我们很容易就能实现 memset
inline void LockzhinerMemorySet(void* dst, unsigned char value, size_t size) {
#if __ARM_NEON
// NEON 优化部分
unsigned char* neon_dst = (unsigned char*)dst;
int nn = size / 64;
size -= nn * 64;
// 生成包含 16 个相同字节的 NEON 向量
uint8x16_t neon_value = vdupq_n_u8(value);
// 使用 NEON 指令进行批量内存填充
while (nn--) {
// 预取下一块数据,尽管在 memset 中这个可能影响较小
__builtin_prefetch(neon_dst + 64);
vst1q_u8(neon_dst, neon_value);
vst1q_u8(neon_dst + 16, neon_value);
vst1q_u8(neon_dst + 32, neon_value);
vst1q_u8(neon_dst + 48, neon_value);
neon_dst += 64;
}
// 处理剩余的字节
while (size >= 16) {
vst1q_u8(neon_dst, neon_value);
neon_dst += 16;
size -= 16;
}
while (size >= 8) {
uint8x8_t neon_value8 = vdup_n_u8(value);
vst1_u8(neon_dst, neon_value8);
neon_dst += 8;
size -= 8;
}
// 处理剩余不足 8 个字节的情况
while (size--) {
*neon_dst++ = value;
}
#else
// 如果不支持 NEON,使用标准的 memset
memset(dst, value, size);
#endif
}