【发布时间】:2018-03-14 14:48:25
【问题描述】:
考虑这两种可以表示“可选int”的方法:
using std_optional_int = std::optional<int>;
using my_optional_int = std::pair<int, bool>;
鉴于这两个功能...
auto get_std_optional_int() -> std_optional_int
{
return {42};
}
auto get_my_optional() -> my_optional_int
{
return {42, true};
}
...g++ trunk 和 clang++ trunk (使用 -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
为什么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;
}
...结果:
main:
mov DWORD PTR a[rip], 42
xor eax, eax
mov DWORD PTR b[rip], 42
ret
【问题讨论】:
-
optional通过隐藏指针返回,这意味着类型定义包含禁止通过寄存器返回的内容。 -
明显的区别是
std::pair是一个聚合,而std::optional不是。不知道它是否应该有效果,但你知道... -
与
boost::optional相同的问题,在任何版本的 GCC 上,演示都不需要花哨的 C++17:godbolt.org/g/MV14mr -
聚合与非聚合类型、SYS V x64 ABI 以及 4294967338 是 0x10000002a 的事实应该清楚地说明这一点。
-
@WojciechMigda
folly::Optional没有必要的魔法来使其特殊的成员函数有条件地变得微不足道。 (它还通过在内联函数中使用内部链接None违反了 ODR,并且每个constexpr或FOLLY_CPP14_CONSTEXPR函数都是格式错误的 NDR:您不能使用optional的constexprAPIaligned_storage.) +1 为co_await-able,但他们最好从 range-v3 窃取optional实现并添加其 API 的其余部分。
标签: c++ performance assembly x86-64 c++17