【问题标题】:Unpacking 8 to 16-bit using SIMD: AVX2 version mixes up the order使用 SIMD 将 8 位解包到 16 位:AVX2 版本混淆了顺序
【发布时间】:2021-10-19 16:24:02
【问题描述】:

我正在尝试使用 SSE2 解压缩带有零的文本,并将其扩展到 AVX2。这就是我的意思:

假设你有一些这样的文本:abcd

我正在尝试使用 SSE2 将 abcd 解压缩为 a\0b\0c\0d\0 是零。这当然适用于 16 个字符而不是 4 个。

我可以使用这段代码做到这一点(忽略 C 风格的演员表):

__m128i chunk = _mm_loadu_si128((__m128i const*) src); // Load 16 bytes from memory

__m128i half = _mm_unpacklo_epi8(chunk, _mm_setzero_si128()); // Unpack lower 8 bytes with zeros
_mm_storeu_si128((__m128i*) dst, half); // Write to destination

half = _mm_unpackhi_epi8(chunk, _mm_setzero_si128()); // Unpack higher 8 bytes with zeros
_mm_storeu_si128((__m128i*) (dst + 16), half); // Write to destination

这很好用,但我正在尝试将代码转换为 AVX2,因此我一次可以处理 32 个字节。但是,我在解压低字节时遇到了麻烦。

这是我用于 AVX2 的代码:

__m256i chunk = _mm256_loadu_si256((__m256i const*) src); // Load 32 bytes from memory

__m256i half = _mm256_unpacklo_epi8(chunk, _mm256_setzero_si256()); // Unpack lower 16 bytes with zeros
_mm256_storeu_si256((__m256i*) dst, half); // Write to destination

half = _mm256_unpackhi_epi8(chunk, _mm256_setzero_si256()); // Unpack higher 16 bytes with zeros
_mm256_storeu_si256((__m256i*) (dst + 32), half); // Write to destination

问题是,_mm256_unpacklo_epi8 指令似乎每转换 8 个字节就跳过 8 个字节。例如这个文本(末尾的“fr”是有意的):

Permission is hereby granted, fr

转换成

Permissireby graon is hented, fr

每 8 个字节 _mm256_unpacklo_epi8,进程,8 个字节被跳过。

我在这里做错了什么? 任何帮助将不胜感激。

【问题讨论】:

  • _mm256_unpacklo_epi8 在通道内运行。使用vpmovzxbw (_mm256_cvtepu8_epi16) 或使用vpermq 来修复解包结果。 (或者将你的负载与_mm_loadu_si128 / _mm256_insertf128_si256 交错)
  • @PeterCordes 但是_mm256_cvtepu8_epi16 接受的是 __m128i,而不是 __m256i。我是否改为加载两次?
  • 是的,加载 128 位块。现代 CPU 的加载执行单元是存储执行单元的两倍。 (除了 Ice Lake,它增加了第二个存储单元。)每个存储 1 个负载是一个找到平衡。
  • @PeterCordes 哦,好的。我要试试这个,谢谢!
  • 我认为这种问题以前出现过,所以我正在寻找一个副本。 unexpected _mm256_shuffle_epi with __256i vectors 涵盖了为什么会发生这种情况,但没有提到好的解决方法。 ASCIIrev32B in this answer 显示了 2x _mm_loadu_si128 => vinserti128 设置的变体,用于通道内随机播放。

标签: c++ simd sse avx2


【解决方案1】:

正如我所见,@PeterCordes 已经收到了正确的答案。不过我想用小的辅助函数来补充它:

template <int part> inline __m256i Cvt8uTo16u(__m256i a)
{
    return _mm256_cvtepu8_epi16(_mm256_extractf128_si256(a, part));
}

【讨论】:

  • 您是否确认编译器正在优化 _mm256_extractf128_si256( a, 0 )?如果不是,手动操作很容易,使用 if constexpr( part == 0 )_mm256_castsi256_si128
猜你喜欢
  • 2020-02-15
  • 1970-01-01
  • 1970-01-01
  • 2012-06-06
  • 2010-11-15
  • 2015-04-24
  • 2015-03-22
  • 2018-01-18
  • 1970-01-01
相关资源
最近更新 更多