Vit*_*meo 65 c++ performance assembly x86-64 c++17
考虑这两种可以代表"可选int
"的方法:
using std_optional_int = std::optional<int>;
using my_optional_int = std::pair<int, bool>;
Run Code Online (Sandbox Code Playgroud)
鉴于这两个功能......
auto get_std_optional_int() -> std_optional_int
{
return {42};
}
auto get_my_optional() -> my_optional_int
{
return {42, true};
}
Run Code Online (Sandbox Code Playgroud)
... g ++ trunk和clang ++ trunk (with -std=c++17 -Ofast -fno-exceptions -fno-rtti
)产生以下程序集:
get_std_optional_int():
mov rax, rdi
mov DWORD PTR [rdi], 42
mov BYTE PTR [rdi+4], 1
ret
get_my_optional():
movabs rax, 4294967338 // == 0x 0000 0001 0000 002a
ret
Run Code Online (Sandbox Code Playgroud)
为什么get_std_optional_int()
需要三个mov
指令,而get_my_optional()
只需要一个movabs
?这是QoI问题,还是说std::optional
规范中存在阻止此优化的问题?
另请注意,无论如何,功能的用户可能会完全优化:
volatile int a = 0;
volatile int b = 0;
int main()
{
a = get_std_optional_int().value();
b = get_my_optional().first;
}
Run Code Online (Sandbox Code Playgroud)
...结果是:
main:
mov DWORD PTR a[rip], 42
xor eax, eax
mov DWORD PTR b[rip], 42
ret
Run Code Online (Sandbox Code Playgroud)
Cas*_*sey 42
libstdc ++显然没有实现P0602"变体和可选应该传播复制/移动平凡".您可以通过以下方式验证:
static_assert(std::is_trivially_copyable_v<std::optional<int>>);
Run Code Online (Sandbox Code Playgroud)
对于libstdc ++来说失败了,并传递了libc ++和MSVC标准库(它确实需要一个合适的名称,因此我们不必将其称为"C++标准库的MSVC实现"或"MSVC STL").
当然,MSVC 仍然不会通过optional<int>
寄存器,因为MS ABI.
编辑:此问题已在GCC 8发布系列中修复.
Jes*_*ter 17
为什么
get_std_optional_int()
需要三个mov
指令,而get_my_optional()
只需要一个movabs
?
直接原因是在寄存器中optional
返回时通过隐藏指针pair
返回.那为什么呢?SysV ABI规范,第3.2.3节参数传递说:
如果C++对象具有非平凡的复制构造函数或非平凡的析构函数,则它由不可见的引用传递.
排除C++混乱optional
并不容易,但至少在optional_base
我检查的实现类中似乎有一个非平凡的复制构造函数.
Max*_*kin 15
在通过Agner Fog调用不同C++编译器和操作系统的约定时,它表示复制构造函数或析构函数阻止在寄存器中返回结构.这解释了为什么optional
不在寄存器中返回.
必须有其他东西阻止编译器进行商店合并(将比单词更窄的立即值的连续存储合并到更少的更宽的存储中以减少指令数量)... 更新: gcc bug 82434 - -fstore-merge不会工作可靠.