- iOS/Objective-C 元类和类别
- objective-c - -1001 错误,当 NSURLSession 通过 httpproxy 和/etc/hosts
- java - 使用网络类获取 url 地址
- ios - 推送通知中不播放声音
reinterpret_cast
合法吗? float*
到 __m256*
和访问 float
对象通过不同的指针类型?
constexpr size_t _m256_float_step_sz = sizeof(__m256) / sizeof(float);
alignas(__m256) float stack_store[100 * _m256_float_step_sz ]{};
__m256& hwvec1 = *reinterpret_cast<__m256*>(&stack_store[0 * _m256_float_step_sz]);
using arr_t = float[_m256_float_step_sz];
arr_t& arr1 = *reinterpret_cast<float(*)[_m256_float_step_sz]>(&hwvec1);
hwvec1
和
arr1
依赖
undefined behavior
?
__m256 hwvec2 = _mm256_load_ps(&stack_store[0 * _m256_float_step_sz]);
_mm256_store_ps(&stack_store[1 * _m256_float_step_sz], hwvec2);
最佳答案
ISO C++ 没有定义 __m256
,所以我们需要看看是什么在支持它们的实现上定义了它们的行为。
英特尔的内在函数将 vector 指针(如 __m256*
)定义为允许为其他任何对象设置别名,与 ISO C++ 将 char*
定义为允许别名的方式相同。
所以是的,取消引用 __m256*
而不是使用 _mm256_load_ps()
对齐加载内部函数是安全的。
但特别是对于 float/double,使用内在函数通常更容易,因为它们也负责从 float*
进行转换。对于整数,AVX512 加载/存储内在函数被定义为采用 void*
,但在此之前您需要一个额外的 (__m256i*)
,这只是很多困惑。
在 gcc 中,这是通过使用 __m256
属性定义 may_alias
来实现的:来自 gcc7.3 的 avxintrin.h
(<immintrin.h>
包含的头文件之一):
/* The Intel API is flexible enough that we must allow aliasing with other
vector types, and their scalar components. */
typedef float __m256 __attribute__ ((__vector_size__ (32),
__may_alias__));
typedef long long __m256i __attribute__ ((__vector_size__ (32),
__may_alias__));
typedef double __m256d __attribute__ ((__vector_size__ (32),
__may_alias__));
/* Unaligned version of the same types. */
typedef float __m256_u __attribute__ ((__vector_size__ (32),
__may_alias__,
__aligned__ (1)));
typedef long long __m256i_u __attribute__ ((__vector_size__ (32),
__may_alias__,
__aligned__ (1)));
typedef double __m256d_u __attribute__ ((__vector_size__ (32),
__may_alias__,
__aligned__ (1)));
__m256*
就像
_mm256_store_ps
,而不是
storeu
。)
may_alias
的 GNU C native vector 为其标量类型设置别名,例如即使没有
may_alias
,您也可以安全地在
float*
和假设的
v8sf
类型之间进行转换。但是
may_alias
可以安全地从
int[]
、
char[]
或其他数组中加载。
_mm_storeu_si128( (__m128i*)&arr[i], vec);
要求您创建可能未对齐的指针,如果您尊重它们,这些指针就会出错。并且
_mm_storeu_ps
到非 4 字节对齐的位置需要创建一个未对齐的
float*
。
__m*
类型和
float*
/
double*
。这对于针对任何普通现代 CPU 的编译器来说是微不足道的,包括具有扁平内存模型(无分段)的 x86; asm 中的指针只是与数据保存在相同寄存器中的整数。 (m68k 具有地址和数据寄存器,但它永远不会因为在 A 寄存器中保留不是有效地址的位模式而出错,只要您不取消引用它们。)
may_alias
与 char*
别名规则一样,只有一种方式 :不能保证使用
int32_t*
读取
__m256
是安全的。使用
float*
读取
__m256
甚至可能不安全。就像执行
char buf[1024];
int *p = (int*)buf;
不安全一样。
char*
读/写可以为任何东西设置别名,但是当你有一个
char
对象时,严格别名确实使它成为通过其他类型读取它的 UB。 (我不确定 x86 上的主要实现是否确实定义了该行为,但您不需要依赖它,因为它们将 4 个字节的
memcpy
优化为
int32_t
。您可以并且应该使用
memcpy
来表达未对齐的负载来自
char[]
缓冲区,因为允许使用更宽类型的自动矢量化为
int16_t*
假设 2 字节对齐,如果不是,则使代码失败:
Why does unaligned access to mmap'ed memory sometimes segfault on AMD64? )
_mm_insert_epi16
/
_mm_extract_epi16
或 SSE4.1 insert/
_mm_extract_epi8/32/64
。对于 float,没有插入/提取内部函数可以与标量
float
一起使用。
[]
运算符,例如
__m256 v = ...;
v[3] = 1.25;
。 MSVC 将 vector 类型定义为带有
.m128_f32[]
成员的 union ,用于按元素访问。
operator[]
重载,以及运算符
+
/
-
/
*
/
<<
等等。这非常好,特别是对于整数类型,其中不同元素宽度的不同类型使
v1 + v2
以正确的大小工作。 (GNU C native vector 语法对浮点/双 vector 执行此操作,并将
__m128i
定义为带符号 int64_t 的 vector ,但 MSVC 不提供基本
__m128
类型的运算符。)
__m128
定义为正常 union 的方式。
关于c++ - 硬件 SIMD vector 指针和相应类型之间的 `reinterpret_cast` 是未定义的行为吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52112605/
我最近了解到 C++ 标准包含“严格的别名规则”,它禁止通过不同类型的变量引用相同的内存位置。 但是,该标准确实允许 char 类型合法地别名任何其他类型。这是否意味着 reinterpret_cas
我需要将一段代码从 C++ 转换为 VB6。 具体来说,这个: reinterpret_cast(reinterpret_cast(lParam)->hwndParent) 有人能告诉我这在 VB6
我正在编写与对齐相关的代码,如果给定的指针正确对齐,却没有标准的功能测试,这让我感到非常惊讶。 似乎互联网上的大多数代码都使用(long)ptr或 reinterpret_cast(ptr)测试对齐,
这个问题在这里已经有了答案: Why is reinterpret_cast not constexpr? (2 个答案) 关闭去年。 我遇到了以下错误: class NormalClass { p
我recently learned通过 reinterpret_casting 其地址将 POD 重新解释为不同的 POD 是未定义行为。所以我只是想知道 reinterpret_cast 的潜在用例
考虑以下程序: struct A{}; int main() { A a; A b = a; A c = reinterpret_cast(a); } 编译器 (g++14)
我recently learned通过 reinterpret_casting 其地址将 POD 重新解释为不同的 POD 是未定义行为。所以我只是想知道 reinterpret_cast 的潜在用例
它为什么会存在?在什么情况下它不会导致严格的别名违规,在哪些情况下会导致? 我的意思是,如果你在两个不兼容的类型之间进行转换,并且该转换的结果是唯一指向它在整个程序中使用的内存的指针,那么在假设没有其
这两个cast语句是一样的吗?它们产生相同的结果。 const std::int16_t i = 3; char a[ 2 ]; *reinterpret_cast(a) = i; reinterp
我已经编写了 MyString 和 MyStringConst 类。现在我需要不时地将 MyString 作为 MyStringConst 传递,因此重载强制转换运算符。这是我写的 MyString:
我有一个结构 S,它将两个类型为 T 的固定大小的数组打包在一起。 template struct S { array, 10> x1; array x2; }; 我想获取对大小为 2
如果我已将结构的成员复制到我的类中,我是否可以从我的类转换为结构? #include #include class Buffer { public: void * address;
#include struct A { int a; }; struct I1 : A { int a; }; struct I2 : A { int a; }; struct D : I1, I2
是否可以使用 reinterpret_cast 将 const 限定符仅添加到结构的一个成员。 #include std::pair test; std::pair& ctest = reinter
int main() { class_name object; object.method(); fstream file("writeobject.dat" , ios::o
当我注意到它的输出完全错误时,我正在测试一个简单的编译器。事实上,输出的字节顺序从小变大了。经过仔细检查,违规代码原来是这样的: const char *bp = reinterpret_cast(&
基本思想是创建一个可变大小的数组,在构造时固定,并在单个分配单元中创建另一个类,以减少开销并提高效率。分配缓冲区以适应数组,并使用另一个对象和新放置来构造它们。为了访问数组的元素和另一个对象,使用了指
#include struct I1 { virtual void nb1()=0; virtual ~I1(){} }; struct I2 { virtual void n
我刚刚在使用reinterpret_cast的代码中发现了一个错误,将其更改为dynamic_cast后,问题就消失了。然后,我尝试使用以下代码重现该行为: 1 #include 2 3
我想知道有没有一种不用大量复制就可以解决这个问题的好方法。 例如,假设我有一个字节缓冲区,我在其中保存了很多东西。我会正确地在其中保存 4 个字节的整数和 float 等。 现在,如果我的整数保存在缓
我是一名优秀的程序员,十分优秀!