在C++中,转换为simd类型是否有未定义的行为
Is casting to simd-type undefined behaviour in C++?
在simd教程中,我发现了以下代码片段。
void simd(float* a, int N)
{
// We assume N % 4 == 0.
int nb_iters = N / 4;
__m128* ptr = reinterpret_cast<__m128*>(a); // (*)
for (int i = 0; i < nb_iters; ++i, ++ptr, a += 4)
_mm_store_ps(a, _mm_sqrt_ps(*ptr));
}
现在我的问题是,这条线是(*(未定义的行为吗?由于以下规范(https://en.cppreference.com/w/cpp/language/reinterpret_cast)
每当试图通过AliasedType类型的glvalue读取或修改DynamicType类型对象的存储值时,除非以下情况之一为真,否则行为是未定义的:
- AliasedType和DynamicType相似
- AliasedType是DynamicType的有符号或无符号变体(可能是cv限定的(
- AliasedType是std::byte、(由于C++17(char或unsigned char:这允许将任何对象的对象表示检查为字节数组
在这种情况下,如何防止未定义的行为?我知道我可以std::memcopy,但性能惩罚会使simd变得无用,或者我错了吗?
编辑:请查看副本中的答案(和/或Peter的答案(。我在下面写的内容在技术上是正确的,但在实践中并不真正相关
是的,这将是基于C++标准的未定义行为。编译器可能仍然可以将其作为扩展正确处理(因为SIMD类型和内部函数不是C++标准的一部分(。
为了在不影响速度的情况下安全正确地执行此操作,您可以使用内在函数将4个浮点直接从内存加载到128位寄存器中:
__m128 reg = _mm_load_ps(a);
有关重要的对齐约束:,请参阅"英特尔Intrnsics指南">
__m128 _mm_load_ps (float const* mem_addr)
将128位(由4个压缩的单精度(32位(浮点元素组成(从内存加载到
dst
中。mem_addr
必须在16字节边界上对齐,否则可能会生成一般保护异常。
Intel的intrinsic API定义了强制转换为__m128*
和取消引用的行为:它与同一指针上的_mm_load_ps
相同。
对于float*
和double*
,基本上存在加载/存储内部函数来包装此重新解释强制转换并将对齐信息传递给编译器。
如果支持_mm_load_ps()
,则实现还必须定义问题中代码的行为
我不知道这是否真的记录在任何地方;也许是在英特尔教程或白皮书中,但这是所有编译器公认的行为,我想大多数人都会同意没有定义这种行为的编译器并不完全支持英特尔的内部API。
__m128
类型被定义为may_alias
1,因此与char*
一样,您可以将__m128*
指向任何东西,包括int[]
或任意结构,并且通过它加载或存储而不违反严格的别名。(只要它被16对齐,否则你确实需要_mm_loadu_ps
,或者用类似GNUC的aligned(1)
属性声明的自定义向量类型(。
脚注1:GNU C中的__attribute__((vector_size(16), may_alias))
,并且MSVC不进行基于类型的别名分析。
- 不知道某个东西是否被忽略会引入未定义的行为吗
- 此增量后语句是否会导致未定义的行为?
- Windows 链接器是否使用 LoadLibrary 解析 DLL 中未定义的符号?
- 如何测试 size_t -1 是否未定义,其中 size_t 为 0?
- 在销毁期间从另一个线程调用对象上调用方法是否未定义行为?
- 从 std::string 到 std::array<char,size> 的 memcopy 额外数据是否是一种未定义的行为?
- 负指数是否必然意味着未定义的行为
- 在"printf"中使用标签"h"或"hh"是否涉及未定义的
- 在 C++17 中,是否未定义使用无锁原子学保护从信号处理程序传递的数据?
- C++ 如何检查 char 变量是否未定义(未初始化)
- 递减 std::vector::begin 是否未定义,即使它从未被使用过?
- 访问从联合与另一个成员集复制的联合中的一个成员是否未定义或未指定?
- 在C++中,转换为simd类型是否有未定义的行为
- 是否是从等待返回到悬而未决的"this"实例的未定义行为?
- 是否未定义将对函数范围变量的引用作为值返回
- FBString 的小字符串优化是否依赖于未定义的行为?
- 使用无效指针初始化指针声明符的行为是否未定义?
- 在 C 和 C++ 中,使用逗号运算符的表达式是否未定义"a = b, ++a;"?
- 初始化变量列表中的赋值顺序是否未定义
- f(++i,++i)是否未定义