我目前正在开发一个操纵图像的项目。为了加快这个过程(并增加我的知识),我决定使用SIMD指令编写一些基本功能。
使用 for loops 的代码是
int idx;
uint16_t* A, B, C;
float gAlpha = 0.8;
float alpha = 0.2;
for (size_t rw = 0; rw < height; rw++) {
for (size_t cl = 0; cl < width; cl++) {
idx = rw * width + height;
C[idx] = static_cast<uint16_t>(gAlpha * static_cast<float>(A[idx]) + alpha * static_cast<float>(B[idx]));
}
}
}
这个循环可能并不完美,但它完美地完成了它的工作,我的单元测试给了我预期的结果。
正如我所说,我正在尝试使用SIMD内部转换这些循环。这是我的工作代码,正如您将看到的,它不是很漂亮......我们可以访问固有的AVX2。
size_t n_pixels = height * width;
for (size_t px = 0; px < n_pixels; px += 8) {
__m128i xlo = _mm_unpacklo_epi16(_mm_load_si128((__m128i*)&A[px]), _mm_set1_epi16(0));
__m128i xhi = _mm_unpackhi_epi16(_mm_load_si128((__m128i*)&A[px]), _mm_set1_epi16(0));
__m128 ylo = _mm_cvtepi32_ps(xlo);
__m128 yhi = _mm_cvtepi32_ps(xhi);
__m256 pxMinFl = _mm256_castps128_ps256(ylo);
pxMinFl = _mm256_insertf128_ps(pxMinFl, yhi, 1);
xlo = _mm_unpacklo_epi16(_mm_load_si128((__m128i*)&B[px]), _mm_set1_epi16(0));
xhi = _mm_unpackhi_epi16(_mm_load_si128((__m128i*)&B[px]), _mm_set1_epi16(0));
ylo = _mm_cvtepi32_ps(xlo);
yhi = _mm_cvtepi32_ps(xhi);
__m256 pxMaxFl = _mm256_castps128_ps256(ylo);
pxMaxFl = _mm256_insertf128_ps(pxMaxFl, yhi, 1);
__m256 avGain1 = _mm256_set1_ps(gAlpha);
__m256 avGain2 = _mm256_set1_ps(alpha);
__m256 prodUp = _mm256_mul_ps(prodUp, avGain1);
__m256 prodBt = _mm256_mul_ps(prodBt, avGain2);
__m256 pxOutFl = _mm256_add_ps(prodUp, prodBt);
__m128 ylo_ps = _mm256_castps256_ps128(pxOutFl);
__m128 yhi_ps = _mm256_extractf128_ps(pxOutFl, 1);
__m128i xlo_ep = _mm_cvtps_epi32(ylo_ps);
__m128i xhi_ep = _mm_cvtps_epi32(yhi_ps); <- POINT 1
int* xl = reinterpret_cast<int*>(&xlo_ep); <- POINT 2
for (int i=0; i < 8; i++) { <- POINT 2
C[px + i] = static_cast<uint16_t>(xl[i]); <- POINT 2
}
}
可能会对此代码进行大量优化,但我已检查 pxOutFl 的输出是否与预期值相对应。当我看到如何将数据保存回输出数组C时,开始看起来像是黑魔法的地方。首先,如果我在POINT 1处注释该行,则代码不起作用即使您可以阅读,我也不会使用该变量。其次,我猜想有一个更好的解决方案,而不是我用来将数据存储回uint16_t数组的技巧(POINT 2),但我找不到一个有效的方法。
有人能指出我正确的方向吗?我错过了什么?我怎么能改进这段代码?
提前致谢!
PS:我们在Linux上使用英特尔编译器2017 for parallel studio专业版2117(Fedora 25)。
答案 0 :(得分:1)
您可以将所有POINT 2重写为:
_mm_storeu_si128((__m128i *)&C[px], xlo_ep);
另请注意,_mm_load_si128
的所有实例都应该是_mm_loadu_si128
,因为您似乎无法保证在任何地方对齐。