我想使用 AVX2 将 2d 数组从 16 位缩小到 8 位。有效的C++代码如下:
auto * s = reinterpret_cast<uint16_t *>(i_frame.Y);
auto * d = narrowed.data();
for (auto y = 0; y < i_frame.Height; y++, s += i_frame.Pitch_Luma / 2, d += o_frame.Width)
{
for (auto x = 0; x < i_frame.Width; x++)
{
d[x] = static_cast<uint8_t>(s[x]);
}
}
然后我想也许使用 AVX2 会更有效(我们所有的系统都有 AVX2 支持):
auto * s = reinterpret_cast<uint16_t *>(i_frame.Y);
auto * d = narrowed.data();
for (auto y = 0; y < i_frame.Height; ++y, s += i_frame.Pitch_Luma / 2, d += o_frame.Width)
{
for (auto x = 0; x < i_frame.Width; x += 16)
{
auto src = _mm256_load_si256(reinterpret_cast<const __m256i *>(s + x));
auto v = _mm256_packus_epi16(src, _mm256_setzero_si256());
v = _mm256_permute4x64_epi64(v, _MM_SHUFFLE(3, 1, 2, 0));
_mm_store_si128(reinterpret_cast<__m128i *>(d + x), _mm256_extracti128_si256(v, 0));
}
}
问题是我的 AVX2 转换代码是否是最佳的和/或正确的方法。我可能缺少一个 AVX2 命令,该命令使这变得非常简单。至少我支持扩大转变。
vpackuswb
和 vpermq
对此很好,但您可以安排一些事情,以便使用相同的说明完成双倍的工作:
for (size_t x = 0; x < width; x += 32)
{
auto src1 = _mm256_load_si256(reinterpret_cast<const __m256i *>(s + x));
auto src2 = _mm256_load_si256(reinterpret_cast<const __m256i *>(s + x + 16));
auto v = _mm256_packus_epi16(src1, src2);
v = _mm256_permute4x64_epi64(v, _MM_SHUFFLE(3, 1, 2, 0));
_mm256_store_si256(reinterpret_cast<__m256i *>(d + x), v);
}
这可能不是一个完全的替代品,因为展开因子发生了变化,因此这可能需要在图像边缘附近额外小心。如果目标仅 16 对齐(或者如果可能的话增加对齐),您可能还需要未对齐的存储。