SIMD -> uint16_t 数组到浮点数组在浮点上工作,然后返回到 uint16_t
Posted
技术标签:
【中文标题】SIMD -> uint16_t 数组到浮点数组在浮点上工作,然后返回到 uint16_t【英文标题】:SIMD -> uint16_t array to float array work on float then back to uint16_t 【发布时间】:2017-09-01 14:23:40 【问题描述】:我目前正在从事一个处理图像的项目。为了加快这个过程(并增加我的知识),我决定使用 SIMD 指令编写一些基本功能。
使用for循环的代码是
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 (Fedora 25) 上使用 Intel compiler 2017 for parallel studio Professional edition 2117。
【问题讨论】:
这样的混合在定点上很容易,实际上更容易,因为不会改变车道宽度。 【参考方案1】:您可以将所有 POINT 2 重写为:
_mm_storeu_si128((__m128i *)&C[px], xlo_ep);
还请注意,_mm_load_si128
的所有实例可能都应该是 _mm_loadu_si128
,因为您似乎无法保证任何地方的对齐。
【讨论】:
以上是关于SIMD -> uint16_t 数组到浮点数组在浮点上工作,然后返回到 uint16_t的主要内容,如果未能解决你的问题,请参考以下文章
从uint32_t [16]数组到uint32_t变量序列的64位副本