C++ 如何避免SSE管道冲洗?

C++ 如何避免SSE管道冲洗?,c++,sse,C++,Sse,我在苏格兰和南方能源公司遇到了一个非常微妙的问题。在这种情况下,我想用SSE优化我的光线跟踪器,这样我就可以基本了解如何用SSE提高性能 我想从这个函数开始 Vector3f Add( const Vector3f& v0 , Vector3f& v1 ); (实际上,我首先尝试优化交叉积,为了简单起见,这里显示了加法,我知道这不是我的光线跟踪器的瓶颈。) 以下是结构定义的一部分: struct Vector3f { union { struct{ float x ; floa

我在苏格兰和南方能源公司遇到了一个非常微妙的问题。在这种情况下,我想用SSE优化我的光线跟踪器,这样我就可以基本了解如何用SSE提高性能

我想从这个函数开始

Vector3f Add( const Vector3f& v0 , Vector3f& v1 );
(实际上,我首先尝试优化交叉积,为了简单起见,这里显示了加法,我知道这不是我的光线跟踪器的瓶颈。)

以下是结构定义的一部分:

struct Vector3f
{ union { struct{ float x ; float y ; float z; float reserved; }; __m128 data; };
问题是SSE寄存器将与此声明保持一致,编译器不够聪明,无法保存这些SSE寄存器以供进一步使用。 使用以下声明,可以避免冲洗

__m128 Add( __m128 v0_data, __m128 v1_data );
在这种情况下,我可以采用这种方式,但对于包含四个m128数据的矩阵来说,这将是一种丑陋的设计。你不能让操作符作用于向量3f本身,而是作用于它的数据:(

最令人不安的是,为了适应这种变化,你将不得不在任何地方更改更高级别的代码。而这种通过SSE进行优化的方式对于大型游戏引擎来说绝对不是一种选择,在它工作之前,你将更改大量的代码


在不避免SSE寄存器刷新的情况下,它的能力将被那些无用的刷新命令耗尽,我想这会使SSE变得无用。

在这里使用联合似乎是一件坏事。只要编译器看到用某个东西统一起来的
\uu m128
,它就会在理解何时更新值方面出现问题,从而导致过度使用e内存操作

在这种情况下,MSVC并不是性能最差的编译器。只需检查一下,它的运行速度比MSVC2013在我的机器上生成的代码慢12倍(这是寄存器溢出),比最佳代码慢20多倍

有趣的是,大多数编译器只有在真正使用
x
y
z
成员访问数据时才会开始做傻事。例如,MSVC2013只有在计算后通过标量成员读取时才会溢出寄存器(我想是为了确保这些成员是实际的)。如果您使用
\u mm\u setr\u ps
设置初始值,而不是将它们直接写入成员,则上述GCC的可怕行为就会消失

在这种情况下,最好避免联合。似乎OP也做出了相同的决定(请参阅)。使访问单个坐标变得更困难具有良好的“心理”性能效果:一个人在编写标量代码之前会三思。您可以轻松地使用提取/插入内部函数编写setter/getter(使编译器生成这些指令),或使用简单的指针算法(使编译器选择某种方式):


当我删除union并简单地使用
\uuu m128
时,生成的代码在所有编译器上都会变得更好。但是,MSVC2013仍然有不必要的移动:每个算术运算都有一个无用的寄存器移动。我想这是编译器内联算法的低效。您可以在MSVC2013中通过声明所有f来删除这些移动函数为。请注意,如果您的simd函数根本没有内联,使用这种新的调用约定还可以避免注册溢出。

@JerryCao1985您可以在问题中直接提及这一点,方法是编辑它,而不是将其添加为注释。如果您对simd优化持认真态度,则需要做好准备完全重新考虑您的代码,以便您可以完全在SIMD中均匀地处理大块数据。尝试以特殊方式将SIMD应用于现有的代码库通常是次优的,正如您在上面的示例中已经看到的那样。注意:您不能既有蛋糕又有蛋糕。它肯定是大型游戏引擎的一个选项,而且是的,SIMD入侵并试图“污染”你的所有代码。就这样吧。只要从一开始就记住这一点,你就会没事的。。
float getX() const { return ((float*)&data)[0]; }