繁体   English   中英

为什么std :: optional <int>的构造比std :: pair <int,bool>更昂贵?

[英]Why is the construction of std::optional<int> more expensive than a std::pair<int, bool>?

考虑这两种可以表示“可选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 ++ trunkclang ++ 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

godbolt.org上的实例


为什么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

libstdc ++显然没有实现P0602“变体和可选应该传播复制/移动平凡” 您可以通过以下方式验证:

static_assert(std::is_trivially_copyable_v<std::optional<int>>);

对于libstdc ++来说失败了,并传递了libc ++和MSVC标准库 (它确实需要一个合适的名称,因此我们不必将其称为“C ++标准库的MSVC实现”或“MSVC STL”)。

当然,MSVC 仍然不会在寄存器中传递optional<int> ,因为MS ABI。

编辑:此问题已在GCC 8发布系列中修复。

为什么get_std_optional_int()需要三个mov指令,而get_my_optional()只需要一个movabs

直接原因是optional通过隐藏指针返回,而pair在寄存器中返回。 那为什么呢? SysV ABI规范,第3.2.3参数传递说:

如果C ++对象具有非平凡的复制构造函数或非平凡的析构函数,则它由不可见的引用传递。

排除optional的C ++混乱并不容易,但至少在我检查的实现的optional_base类中似乎有一个非平凡的复制构造函数

通过Agner Fog调用不同C ++编译器和操作系统的约定时,它表示复制构造函数或析构函数阻止在寄存器中返回结构。 这解释了为什么在寄存器中不返回optional

必须有其他东西阻止编译器进行商店合并( 将比单词更窄的立即值的连续存储合并到更少的更宽的存储中以减少指令数量 )... 更新: gcc bug 82434 - -fstore-merge不会工作可靠。

即使std::is_trivially_copyable_v<std::optional<int>>为false, 技术上也允许优化 但是,编译器可能需要不合理程度的“聪明”才能找到。 此外,对于使用std::optional作为函数的返回类型的特定情况,可能需要在链接时而不是编译时完成优化。

执行此优化不会影响任何(明确定义的)程序的可观察行为*,因此在as-if规则下隐式允许。 但是,由于其他答案中解释的原因,编译器尚未明确地意识到这一事实,需要从头开始推断。 行为静态分析本身就很困难 ,因此编译器可能无法证明此优化在所有情况下都是安全的。

假设编译器可以找到此优化,则需要更改此函数的调用约定(即更改函数返回给定值的方式),这通常需要在链接时完成,因为调用约定会影响所有调用站点。 或者,编译器可以完全内联函数,这可能在编译时可能也可能不可能。 对于易于复制的对象,这些步骤不是必需的,因此在这个意义上,标准确实会抑制优化并使其复杂化。

std::is_trivially_copyable_v<std::optional<int>>应该是真的。 如果确实如此,编译器将更容易发现并执行此优化。 那么,回答你的问题:

这是QoI问题,还是std::optional的规范中存在阻止此优化的问题?

这两者都是。 该规范使得优化更难以找到,并且实现不够“智能”足以在这些约束下找到它。


*假设你没有做过一些非常奇怪的事情,比如#define int something_else

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM