ARM Neon intrinsics转x86/x64 Windows代码正确性咨询
代码转换正确性分析:ARM Neon均值缩放函数转Windows x86/x64
原ARM Neon函数的核心逻辑
原函数针对通道交错的输入数据(如RGBRGBRGB...,每个像素占连续3个float),将其转换为通道分离的输出格式(先存储所有像素的通道0,再通道1,最后通道2),同时完成均值减法和缩放操作:
void NeonMeanScale(const float *din, float *dout, int size, const std::vector<float> mean, const std::vector<float> scale) { if (mean.size() != 3 || scale.size() != 3) { std::cerr << "[ERROR] mean or scale size must equal to 3" << std::endl; exit(1); } float32x4_t vmean0 = vdupq_n_f32(mean[0]); float32x4_t vmean1 = vdupq_n_f32(mean[1]); float32x4_t vmean2 = vdupq_n_f32(mean[2]); float32x4_t vscale0 = vdupq_n_f32(scale[0]); float32x4_t vscale1 = vdupq_n_f32(scale[1]); float32x4_t vscale2 = vdupq_n_f32(scale[2]); float *dout_c0 = dout; float *dout_c1 = dout + size; float *dout_c2 = dout + size * 2; int i = 0; for (; i < size - 3; i += 4) { float32x4x3_t vin3 = vld3q_f32(din); float32x4_t vsub0 = vsubq_f32(vin3.val[0], vmean0); float32x4_t vsub1 = vsubq_f32(vin3.val[1], vmean1); float32x4_t vsub2 = vsubq_f32(vin3.val[2], vmean2); float32x4_t vs0 = vmulq_f32(vsub0, vscale0); float32x4_t vs1 = vmulq_f32(vsub1, vscale1); float32x4_t vs2 = vmulq_f32(vsub2, vscale2); vst1q_f32(dout_c0, vs0); vst1q_f32(dout_c1, vs1); vst1q_f32(dout_c2, vs2); din += 12; dout_c0 += 4; dout_c1 += 4; dout_c2 += 4; } for (; i < size; i++) { *(dout_c0++) = (*(din++) - mean[0]) * scale[0]; *(dout_c1++) = (*(din++) - mean[1]) * scale[1]; *(dout_c2++) = (*(din++) - mean[2]) * scale[2]; } }
转换后的Windows代码的错误
转换后的代码错误地假设输入din是通道分离格式(先所有通道0像素,再通道1,再通道2),但实际输入来自OpenCV的cv::Mat,是通道交错格式(和原ARM代码的输入布局一致),这导致数据读取逻辑完全错误,计算结果混乱:
void NeonMeanScale(const float *din, float *dout, int size, const std::vector<float> mean, const std::vector<float> scale) { if (mean.size() != 3 || scale.size() != 3) { std::cerr << "[ERROR] mean or scale size must equal to 3" << std::endl; exit(1); } for (int i = 0; i < size; ++i) { dout[i] = (din[i] - mean[0]) * scale[0]; dout[size + i] = (din[size + i] - mean[1]) * scale[1]; dout[size * 2 + i] = (din[size * 2 + i] - mean[2]) * scale[2]; } }
修正后的Windows代码
修正后的代码严格对齐原ARM函数的逻辑,处理通道交错的输入,输出通道分离的结果:
void MeanScale(const float *din, float *dout, int size, const std::vector<float> mean, const std::vector<float> scale) { if (mean.size() != 3 || scale.size() != 3) { std::cerr << "[ERROR] mean or scale size must equal to 3" << std::endl; exit(1); } float *dout_c0 = dout; float *dout_c1 = dout + size; float *dout_c2 = dout + size * 2; for (int i = 0; i < size; ++i) { // 读取当前像素的3个通道(交错格式),计算后写入对应通道的输出区域 *(dout_c0++) = (*din++ - mean[0]) * scale[0]; *(dout_c1++) = (*din++ - mean[1]) * scale[1]; *(dout_c2++) = (*din++ - mean[2]) * scale[2]; } }
修正说明
- 输入
din按交错格式读取:每个像素占连续3个float,依次为通道0、通道1、通道2; - 输出保持通道分离格式:
dout_c0存储所有像素的通道0结果,dout_c1存储通道1,dout_c2存储通道2; - 逻辑完全匹配原ARM代码的逐像素处理分支,去掉Neon批量指令后,功能和原代码一致。
内容的提问来源于stack exchange,提问作者Prashant
相关产品推荐
相关产品推荐

