当前位置:  开发笔记 > 编程语言 > 正文

在C ++中是否转换为simd类型的未定义行为?

如何解决《在C++中是否转换为simd类型的未定义行为?》经验,为你挑选了2个好方法。

在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失去作用,或者我错了吗?



1> Max Langhof..:

编辑:请查看重复项中的答案(和/或此处的彼得的答案)。我在下面写的内容在技术上是正确的,但实际上并没有真正的意义。


是的,这将是基于C ++标准的未定义行为。您的编译器可能仍会正确地将其作为扩展进行处理(首先发现SIMD类型和内部函数不是C ++标准的一部分)。

为了安全,正确地执行此操作而不影响速度,可以使用固有函数将4个浮点数直接从内存加载到128位寄存器中:

__m128 reg = _mm_load_ps(a);

有关重要的对齐约束,请参阅《英特尔技术指南》:

__m128 _mm_load_ps (float const* mem_addr)

将内存中的128位(由4个压缩的单精度(32位)浮点元素组成)加载到中dstmem_addr必须在16字节边界上对齐,否则可能会产生一般保护异常。



2> Peter Cordes..:

英特尔的内在API确实定义了强制转换__m128*和取消引用的行为:与_mm_load_ps在同一指针上相同。

对于float*double*,基本上存在加载/存储内在函数来包装此重新解释的类型转换,并将对齐信息传递给编译器。

如果_mm_load_ps()支持,实现还必须定义问题中代码的行为。


我不知道这是否真的记录在任何地方;也许在英特尔的教程或白皮书,但它是商定所有的编译器的行为,我想大多数人都会同意,一个编译器没有定义这种行为并不完全支持英特尔的内部函数API。

__m128类型定义为may_alias1,因此就像char*您可以将a指向__m128*任何对象(包括int[]or任意结构),并通过其加载或存储而不会违反strict-aliasing一样。(只要它与16对齐,否则您确实需要_mm_loadu_ps,或者使用GNU C的aligned(1)属性声明的自定义矢量类型)。


脚注1: __attribute__((vector_size(16), may_alias))在GNU C中,MSVC不进行基于类型的别名分析。

推荐阅读
mobiledu2402851373
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有