【问题标题】:How can I exchange the low 128 bits and high 128 bits in a 256 bit AVX (YMM) register如何在 256 位 AVX (YMM) 寄存器中交换低 128 位和高 128 位
【发布时间】:2011-11-04 19:39:41
【问题描述】:

我正在移植 SSE SIMD 代码以使用 256 位 AVX 扩展,但似乎找不到任何可以混合/混洗/移动高 128 位和低 128 位的指令。

背景故事:

我真正想要的是 VHADDPS/_mm256_hadd_psHADDPS/_mm_hadd_ps 一样,只有 256 位字。不幸的是,它就像对HADDPS 的两次调用一样,独立作用于低位和高位字。

【问题讨论】:

标签: x86 simd avx


【解决方案1】:

我知道这样做的唯一方法是使用_mm256_extractf128_si256_mm256_set_m128i。例如。交换 256 位向量的两半:

__m128i v0h = _mm256_extractf128_si256(v0, 0);
__m128i v0l = _mm256_extractf128_si256(v0, 1);
__m256i v1 = _mm256_set_m128i(v0h, v0l);

【讨论】:

  • 你知道_mm256_extractf128_si256_mm256_extracti128_si256的区别吗?我唯一能说的是第一个适用于 AVX,第二个需要 AVX2。为什么有人会使用第二个版本。我查看了 Agner Fog 的指令表,延迟、吞吐量和端口是相同的。也许我应该问这个问题。
  • 我以为我已经在 SO 上的某个地方看到过这个问题,但快速搜索并没有发现 - AFAIK 它们实际上是相同的。
  • @Zboson:哎呀——刚刚找到我上面提到的问题——我应该搜索指令而不是内在函数:stackoverflow.com/questions/18996827/…
  • 我相信这种方式比马克的回答要慢,因为 extractfset 每个都有 lat 3,吞吐量 1。
  • @mafu:是的,是的 - 另请注意,clang(可能还有其他编译器)足够聪明,可以将上述内容转换为单个 vperm2f128,使其与 Mark 的答案基本相同。跨度>
【解决方案2】:

使用 VPERM2F128,可以交换低 128 位和高 128 位(以及其他排列)。内建函数用法是这样的

x = _mm256_permute2f128_ps( x , x , 1)

第三个参数是一个控制字,它给用户很大的灵活性。详情请见Intel Instrinsic Guide

【讨论】:

  • 英特尔参考手册指定了控制字:VPERM2F128 (direct link) - AVX2 也有 VPERM2I128 基本上是一样的 - 不知道为什么英特尔认为他们需要 2 条不同的指令,因为类型应该没有区别,还是应该这样做?
  • valignq 也可用于在 512 位上以 64 位增量执行 ROR 的等效操作(使用 valignd 来代替 32 位)。
  • @AlexisWilke:这需要 AVX-512。仅使用 AVX2,您就可以使用即时 vpermq 来交换单个向量的一半。 vperm2f128 只需要 AVX1,但在一些 CPU(例如 Zen1 和 KNL)上比 vpermq 慢。
【解决方案3】:
x = _mm256_permute4x64_epi64(x, 0b01'00'11'10);

阅读它here。还有Try it online!

注意:该指令需要AVX2(不仅仅是AVX1)。

@PeterCordes 在 Zen2 / Zen3 CPU 上的 commented _mm256_permute2x128_si256(x, x, i) 是最好的选择,尽管它有 3 个参数,而我建议的函数 _mm256_permute4x64_epi64(x, i) 有 2 个参数。在 Zen1 和 KNL/KNM(以及 Bulldozer 系列挖掘机)上,我建议的 _mm256_permute4x64_epi64(x, i) 效率更高。在其他 CPU(包括主流 Intel)上,这两种选择是相同的。

如前所述,_mm256_permute2x128_si256(x, y, i)_mm256_permute4x64_epi64(x, i) 都需要 AVX2,而 _mm256_permute2f128_si256(x, i) 只需要 AVX1。

【讨论】:

  • 这不仅需要 AVX1,还需要 AVX2,但是是的,它在一些 CPU 上比 VPERM2F128 更快,在其他 CPU 上也是如此。 (包括 Zen1 令人惊讶的 uops.info,以及 2-input shuffle 速度较慢的 Knight's Landing)。我认为没有比这更糟的地方了,除了像 Sandybridge 和 Piledriver 这样只有 AVX1 的 CPU 根本无法运行它。
  • @PeterCordes 感谢您的评论!我将添加一个注释,它需要 AVX2。我只是想,当 OP 写他需要 AVX 指令时,他实际上可能意味着他需要任何版本的 AVX,通常是这种情况。就像有人刚刚说我需要 SSE 解决方案时一样,他实际上是指在大多数情况下 SSE2-SSE4.2。但是,是的,由 OP 来澄清他真正需要什么。我的解决方案仍然对某些人有用。至少对我来说,当我真正需要 avx2 解决方案时,Google 会出现这个问题。
  • 是的,没错,Zen2 / Zen3 _mm256_permute2x128_si256(x, x, i) 是最好的选择,重复相同的输入两次。在 Zen1 和 KNL/KNM(以及 Bulldozer 系列挖掘机)上,_mm256_permute4x64_epi64(x, i) 效率更高。在其他 CPU(包括主流 Intel)上,这两种选择是平等的。 AVX1 CPU 没有选择,只有vperm2f128 可用。甚至vpermpd 也是AVX2。
  • vperm2f128 (AVX1) 和 vperm2i128 (AVX2) 在每个 AVX2 CPU 上运行相同。我认为在任何真正的 CPU 上使用 AVX2 整数指令之间的 f128 版本不会有额外的旁路延迟,但使用 i128 版本可能是个好主意 - 它不应该比 vperm2f128 更糟, 虽然它可能比 vpermq 更糟糕,具体取决于 CPU。
  • 在任何地方都以相同的速度运行 - 我不能 100% 确定这一点。例如,如果您在 vpaddb ymm, ymm 指令之间使用 vperm2f128,则某些 CPU 可能会有额外的延迟。因此,如果您正在使用也需要 AVX2 的其他 __m256i 内在函数,请使用 _mm256_permute2x128_si256_mm256_permute4x64_epi64。如果您在只需要 AVX1(可能还有 FMA)的函数中使用 __m256__m256d,那么只为 vpermpd 制作单独的 AVX2 版本是不值得的,除非您想专门针对 Zen1 进行调整(考虑考虑其 128 位矢量硬件)。
猜你喜欢
  • 2013-01-11
  • 1970-01-01
  • 2017-06-08
  • 1970-01-01
  • 1970-01-01
  • 2017-07-15
  • 1970-01-01
  • 2021-06-26
  • 2011-01-14
相关资源
最近更新 更多