当我惊讶地发现以下情况时,我正在乱搞sse。我试图访问__m128中的各个浮点数。
__m128 x = _mm_set_ps(1.0, 2.0, 3.0, 4.0);
cout << x[1] << endl;
在这里,我可以读取它,就像它是一个数组。 x [0]打印4和x [1]打印三个,这是我期望的向后但这可能与字节顺序有关。我想知道的是这是一种标准/推荐的方式来读取甚至编写向量的各个组件。我在网上看到的所有例子似乎都在使用矢量类型和数组之间的联合。
答案 0 :(得分:4)
不,这不合法,MSDN文档明确警告不要这样做。
在C ++中,您可以使用valarray
。在C中,大多数编译器都支持GCC扩展__attribute__ ((vector_size(N)))
。
自Parallel Studio 2011以来的英特尔编译器,MSVC更长,但不是GCC(虽然GCC和Clang确实支持打字),你可以写:
__m128 result = foo();
float f1 = result.m128_f32[0];
这是否是一般的未定义行为,它可能在你可能关心的编译器上得到支持,并且未来的实现不太可能编译使用m128_f32
但是默默地破坏它的代码。您可以使用其他内在函数(例如_mm_store_ss()
)来提取浮点数。
最便携的解决方案是memcpy()
。
答案 1 :(得分:4)
您提出两个问题:读取/写入x86 SIMD寄存器的顺序以及执行此操作的方法。
您可以像这样
设置大端格式的寄存器值__m128 x = _mm_set_ps(4.0, 3.0, 2.0, 1.0)
或像这样的小端格式
__m128 x = _mm_setr_ps(1.0, 2.0, 3.0, 4.0) // r presumably means reverse
混乱可能来自阵列。我们以小端格式编写/存储数组,而不管硬件如何。我的意思是例如我们写
float xa[] = {1.0, 2.0, 3.0, 4.0};
(x86架构以little-endian格式存储每个float的字节,但这是一个单独的问题。)
因此,只有一种方法可以按顺序加载数组
__m128 x = _mm_loadu_ps(xa);
现在我们可以回答您的其他问题。如果要访问SSE寄存器的多个元素,最好的方法是将值存储到像这样的数组
float t[4]; _mm_storeu_ps(t, x);
由于它存储到一个数组,因此只有一个方法就像加载一样。
使用store intrinsics是我认为最好的解决方案,因为它不依赖于编译器特定的实现。这将适用于C和C ++中的GCC,ICC,Clang和MSVC。这是内在论的重点。它们为您提供类似于组件的功能,这些功能不依赖于某种编译器实现或汇编语法。
但是如果你只想要第一个元素使用_mm_cvtss_f32
。
更多关于enddianess。
如果我们以小端格式编写数字,可能会减少混淆。考虑比较用我们通常写数字的方式(big-endian风格)分别写成的整数:
54321
4321
321
21
1
我们最终通过输入适当的空格来正确地证明这些数字。如果我们使用了little-endian格式,那就是
12345
1234
123
1
这需要更少的工作来写,但我们可能会从右到左阅读数字。