当函数参数是常量引用临时或按值复制的临时时,为什么 MSVC 优化会破坏 SSE 代码
Why do MSVC optimizations break SSE code when function arguments are const refs to temporaries or temporaries copied by value?
昨天遇到这个问题,我将尝试给出清晰简单的例子,这些例子在MSVC12(VS2013,120)和MSVC14(VS2015,140)中失败了。一切都隐含在/arch:SSE+ 和 x64 中。
我将把这个问题琐碎化为一个简单的矩阵转置示例,使用定义的宏_MM_TRANSPOSE4_PS用于说明目的。这是在随机播放方面实现的,而不是移动 L/H 8 字节块。
float4x4 Transpose(const float4x4& m) {
matrix4x4 n = LoadMatrix(m);
_MM_TRANSPOSE4_PS(n.row[0], n.row[1], n.row[2], n.row[3]);
return StoreMatrix(n);
}
matrix4x4
只是一个包含四个__m128
成员的 POD 结构,所有内容都在 16 字节边界上整齐地对齐,即使它有点隐式:
__declspec(align(16)) struct matrix4x4 {
__m128 row[4];
};
所有这些操作在/O1、/O2 和/Ox 上都失败:
// Doesn't work.
float4x4 resultsPlx = Transpose( GiveMeATemporary() );
// Changing Transpose to take float4x4, or copy a temporary
float4x4 Transpose(float4x4 m) { ... }
// Trying again, doesn't work.
float4x4 resultsPlx = Transpose( GiveMeATemporary() );
奇怪的是,这有效:
// A constant reference to an rvalue, a temporary
const float4x4& temporary = GiveMeATemporary();
float4x4 resultsPlx = Transpose(temporary);
基于指针的传输也是如此,这是合乎逻辑的,因为底层机制是相同的。C++11规范的相关部分是§12.2/5:
第二个上下文是当引用绑定到临时时。这 引用绑定到的临时或临时 将 Complete 对象绑定到临时绑定的子对象 在引用的生存期内持续存在,除非下面指定。 临时绑定到构造函数中的引用成员 ctor-initializer (§12.6.2 [class.base.init]) 一直存在到 构造函数退出。临时绑定到 函数调用 (§5.2.2 [expr.call]) 一直持续到完成 包含调用的完整表达式。
这意味着它应该一直存在到调用环境超出范围,这是在函数返回很久之后。那么,什么给了呢?在所有其他情况下,变量会被"优化",但以下情况除外:
Access violation reading location 0xFFFFFFFFFFFFFFFF
虽然解决方案很明显,但可以防止用户像其他一些库一样直接使用基于指针的传输传递临时文件,但我曾希望实际上让它更优雅一点,而不会阻塞视图。
您可以向结构添加(非虚拟)成员函数,而不会真正影响布局。因此,添加析构函数以在结构被破坏时打印"我在这里%p",并在函数中打印"我在那里"。(包括此地址,您可以理解正在使用的其他临时副本)。
然后,您可以在优化的代码中观察生存期。 看看这是否是你的问题:我怀疑糟糕的生命周期实际上意味着什么,因为它所在的位置仍然是堆栈帧中的有效地址。
此外,更改浮点数应该存在的位可能会在最坏的情况下给你一个非数字或特殊值,在这种情况下,向量处理不会抛出或出错,而是放置一个标志值作为该坏元素的结果。 没有指针,那么为什么它取消引用 −1 ?
我认为失火是由更有趣的事情引起的。
在调试器中运行它,看看是什么指令导致这种情况。
- 纯函数,为什么没有优化
- 为什么大多数 pair 实现默认不使用压缩(空基优化)?
- 为什么开关的优化方式与 c/c++ 中的链接不同?
- 为什么我的程序在 O0 和 O2 的优化级别返回不同的结果
- 为什么乘以常量有符号整数分数没有优化?
- 当空基类也是成员变量时,为什么禁止空基优化?
- 为什么 std::chrono 在测量循环和编译器优化的并行 OpenMP 的执行时间时不起作用?
- 为什么优化大型 std::vector 数组会导致 SegFault?
- 为什么警告阻止我们编写优化程序
- 为什么 GCC 不优化删除 C++ 中的空指针?
- 为什么要按值传递string_view?为什么Visual Studio不能优化这一点?
- 为什么编译器不能优化这个 std::string 构造?
- 为什么编译器并不总是优化局部变量?
- 为什么 CLang++ 不优化循环,而 G++ 优化循环?
- 为什么编译器不对此进行优化?
- 为什么 g++ 会对此进行优化?至少,我认为这就是正在发生的事情。
- 为什么 g++ 不优化局部数组而是优化全局数组?
- "constexpr if" vs "if" 与优化 - 为什么需要"constexpr"?
- 优化:为什么<比倍数还贵
- 容器优化:为什么STL容器方法参数不再使用allocator::const_reference typedef