在支持AVX-512和BF 16的中央处理器上,您可以使用512位载体寄存器来存储32个16位浮点数.

我找到了可以将FP 32值转换为BF 16值的内部函数(例如:_mm512_cvtne2ps_pbh),但我还没有找到任何可以直接从内存加载BF 16值的内部函数.如果我总是将这些值转换为BF 16,那么总是在FP 32中加载这些值似乎有点浪费.是否不支持直接BF 16加载,或者我只是还没有找到合适的内在内容?

推荐答案

内在学中奇怪的疏忽. asm中没有针对BH 16的特殊vmov指令,因为您不需要:您只需使用vmovups,因为asm不关心类型. (除了有时是integer与FP域之外,所以可能更喜欢FP加载或存储指令- integer vmovdqu16可能在某些处理器上有一个额外的从加载到FP LU转发的延迟周期.)

如果对齐的加载/存储适用于您的用例,只需将__m512bh*指向您的数据并go 引用它即可.(100 -它被明确定义为等效于对齐的加载或存储固有内容,并且允许别名任何其他数据).

如果不是,那么正如@chtz指出的那样,您可以将memcpy转换到/从__m512bh变量. 现代编译器知道如何内联和优化小的固定大小memcpy,尤其是变量的确切大小. @chtz's demo on Godbolt表明它优化了我们想要的方式,即用海湾合作委员会和clang -O1,就像使用__m512bh*的deref一样,但适用于未对齐.

但对于MSVC来说就不太好了;它工作正常,但本地var的memcpy实际上保留了堆栈空间并将值存储到它,并将其留在ZMM 0中作为返回值. (不重新加载副本,但不将存储和死存储优化为res.)


对于内在演员,甚至没有来自__m512__m512d__m512i的内在演员阵容. (或者对于任何较窄的载体宽度.)

但大多数编译器也允许您对vector类型使用C风格的强制转换,就像这样将比特重新解释(类型双关语)为不同的vector类型:

 __m512bh vec = (__m512bh) _mm512_loadu_ps( ptr );  // Not supported by MSVC

这是not,是由Intel's intrinsics guide定义的标准事物,但GCC和clang至少以与intrinsics API的函数(例如_mm512_castsi512_ps_mm512_castps_ph)相同的方式实现C风格的强制转换(以及C++ std::bit_cast,可能是static_cast)(我们希望BF 16存在的FP 16固有功能).

AVX-512负载内部函数需要void*,这表明可以在任何类型的数据上使用它们. 因此,这只需不转换指针,只需转换载体数据即可.

256位和128位的整值加载/存储分别采用__m256i*__m128i*个指针,FP加载采用float*个指针. 但执行_mm_loadu_ps( (float*)&int_vector[i] )仍然是严格别名安全的. 无论如何,一旦你得到__m256__m128(__m256bh) vec就可以在大多数编译器中工作.

MSVC对这个演员阵容感到窒息. 如果您使用C++,您可能会使用C++20 std::bit_cast<__m512h>( vec ) for MSVC. But if you want to write portable C that compiles efficiently on MSVC as well as GCC/Clang, your only option might be to deref an aligned pointer. memcpy在MSVC上编译到死存储,转换值不起作用,并且向量指针的derref需要在GCC/Clang上对齐. MSVC始终避免对指令版本进行配置判断,因此,如果您愿意使用#ifdef,则可以安全地在MSVC上删除未对齐的__m512h*.

(在没有AVX的情况下对__m128*进行反引用是不安全的,因为它可能会折叠到像addps xmm0, [rdi]这样的内存源操作数中,这确实需要对齐,但这仅适用于遗留SSE的事情. VEX /BEP编码默认允许未对齐. 原始deref不会创造vmovntps个只提供满足需求口味的store ;如果需要vmovxxx,即使已知指针已对齐,它也会使用vmovups而不是vmovaps. 与MSVC和classic ICC不同,当它们能够证明其安全时,就会使用强制执行指令.)

C++相关问答推荐

如何将一个enum类型类型转换为另一个类型?

位屏蔽对于无符号转换是强制的吗?

为什么写入系统调用打印的字符数不正确?

如何在C宏中确定Windows主目录?

如何正确地索引C中的 struct 指针数组?

为什么I2C会发送错误的数据?

#If指令中未定义宏?

双指针指向常量双指针的指针类型赋值不兼容

插座打开MacOS组件

getline()从c中的外部函数传递指针时输出null

C代码可以在在线编译器上运行,但不能在Leetcode上运行

Fprintf正在写入多个 struct 成员,并且数据过剩

从CentOS 7到Raspberry PI 2B的交叉编译-无法让LIBC和System Include标头一起工作

具有正确标头的C struct 定义问题

用于计算位数和的递归C函数

基于蝶数恰好有8个除数的事实的代码

如何在Rust中处理C的longjmp情况?

在列表中查找素数

gdb - 你能找到持有内部 glibc 锁的线程吗?

是什么阻止编译器优化手写的 memcmp()?