我想将两个__m128
值合并为一个__m256
。
像这样的东西:
__m128 a = _mm_set_ps(1, 2, 3, 4);
__m128 b = _mm_set_ps(5, 6, 7, 8);
类似于:
__m256 c = { 1, 2, 3, 4, 5, 6, 7, 8 };
有什么内在函数可以用来做到这一点吗?
这应该做你想要的:
__m128 a = _mm_set_ps(1,2,3,4);
__m128 b = _mm_set_ps(5,6,7,8);
__m256 c = _mm256_castps128_ps256(a);
c = _mm256_insertf128_ps(c,b,1);
如果顺序与您想要的相反,则只需切换a
和b
。
感兴趣的本质是_mm256_insertf128_ps
它可以让您将 128 位寄存器插入 256 位 AVX 寄存器的下半部分或上半部分:
他们的完整家族在这里:
英特尔文档 __m256 _mm256_set_m128(__m128 hi, __m128 lo)
并_mm256_setr_m128(lo, hi)
作为vinsertf128
指令的内在函数,这就是您想要的1。(当然也有__m256d
和__m256i
版本,使用相同的指令。__m256i版本vinserti128
如果有AVX2可以使用,否则它会使用f128。)
如今,所有 4 个主要 x86 编译器(gcc、clang、MSVC 和 ICC)的当前版本都支持这些内在函数。但不是旧版本;与英特尔文档中的其他一些辅助内在函数一样,广泛的实施一直很缓慢。(通常 GCC 或 clang 是最后一个没有你希望可以便携使用的东西的坚持。)
如果您不需要移植到旧 GCC 版本,请使用它:它是表达您想要的内容的最易读的方式,遵循众所周知的_mm_set
模式_mm_setr
。
在性能方面,它当然与手动强制转换 +vinsertf128
内在函数(@Mysticial 的回答)一样有效,而且对于 gcc,至少这实际上是内部.h
实际实现的方式_mm256_set_m128
。
编译器版本支持_mm256_set_m128
/ _mm256_setr_m128
:
https://godbolt.org/z/1na1qr有所有 4 个编译器的测试用例。
__m256 combine_testcase(__m128 hi, __m128 lo) {
return _mm256_set_m128(hi, lo);
}
他们都将此函数编译为 one vinsertf128
,除了 MSVC ,即使最新版本也浪费了vmovups xmm2, xmm1
复制寄存器。(我曾经-O2 -Gv -arch:AVX
使用 vectorcall 约定,因此 args 将在寄存器中,以便为 MSVC 提供有效的非内联函数定义。)如果 MSVC 可以将结果写入第三个寄存器,则推测 MSVC 可以内联到更大的函数中,而不是调用约定强制它读取 xmm0 并写入 ymm0。
脚注 1:
vinsertf128
在 Zen1 上非常高效,与vperm2f128
其他具有 256 位宽 shuffle 单元的 CPU 一样高效。它还可以从内存中取出高半部分,以防编译器溢出它或将 a 折叠_mm_loadu_ps
到其中,而不需要单独将 128 位加载到寄存器中;vperm2f128
的内存操作数将是您不想要的 256 位负载。
即使这个也可以:
__m128 a = _mm_set_ps(1,2,3,4);
__m128 b = _mm_set_ps(5,6,7,8);
__m256 c = _mm256_insertf128_ps(c,a,0);
c = _mm256_insertf128_ps(c,b,1);
由于 c 未初始化,您将收到警告,但您可以忽略它,如果您正在寻找性能,此解决方案将使用比另一个解决方案更少的时钟周期。
也可以使用置换内在函数:
__m128 a = _mm_set_ps(1,2,3,4);
__m128 b = _mm_set_ps(5,6,7,8);
__m256 c = _mm256_permute2f128_ps(_mm256_castps128_ps256(a), _mm256_castps128_ps256(b), 0x20);
我不知道哪种方式更快。
我相信这是最简单的:
#define _mm256_set_m128(/* __m128 */ hi, /* __m128 */ lo) \ _mm256_insertf128_ps(_mm256_castps128_ps256(lo), (hi), 0x1)
__m256 c = _mm256_set_m128(a, b);
请注意__mm256_set_m128
已在 msvc 2019 中定义,如果您#include "immintrin.h"