std::optional<T> 的开销?

Posted

技术标签:

【中文标题】std::optional<T> 的开销?【英文标题】:Overhead of std::optional<T>? 【发布时间】:2014-06-24 17:57:26 【问题描述】:

现在std::experimental::optional 已被接受(或即将被接受),我想知道当以下运算符获取内部值时,会产生什么开销和对程序集的影响:

->
*
value
value_or

与没有std::optional 的情况相比。它对于计算密集型程序可能特别重要。

例如,与 std::vector&lt;double&gt; 相比,std::vector&lt;std::experimental::optional&lt;double&gt;&gt; 上的操作开销的数量级是多少?

【问题讨论】:

你需要更精确的测试用例才能判断。开销可以从可以忽略不计到 10 倍(防止矢量化和其他优化)。但是你不会用同样的方式使用它们,一个有额外的功能,所以它是比较苹果和橘子。 【参考方案1】:

除了其他答案之外,您还应该考虑std::optional 需要额外的内存。

通常它不仅仅是一个额外的字节,而是(至少对于“小”类型)由于填充而导致的 2x 空间开销。

也许 RAM 不是问题,但这也意味着缓存中可用的值更少。

如果特定知识允许使用哨兵值,可能是更好的选择(可能以markable 的形式保持类型安全)。

一个有趣的阅读是:Boost optional - Performance considerations

【讨论】:

【参考方案2】:

-&gt;* 应该有零开销。valuevalue_or 应该有一个分支的开销:if(active) 此外,复制/移动构造函数、复制/移动赋值、swap、emplace、operator==operator&lt; 和析构函数也应该有一个分支的开销。 然而,一组开销是如此之小,以至于可能甚至无法测量。说真的,写漂亮的代码,不要担心这里的性能。使代码漂亮的可能性会导致它运行得比你试图让它更快的速度更快。违反直觉,但无论如何都要这样做。

在某些情况下开销变得明显,例如对大量optionals 进行排序。在这些情况下,有四种情况, (A) 所有已知的选项都提前为空,在这种情况下,为什么要排序? (B) 某些可选选项可能处于活动状态,也可能不处于活动状态,在这种情况下,开销是必需的,并且没有更好的方法。 (C) 所有选项都提前知道有值,并且您不需要排序数据,在这种情况下,使用零开销运算符制作副本使用原始类型的数据而不是optional,然后对那个进行排序。 (D) 已知所有选项都提前具有值,但您需要就地排序数据。在这种情况下,optional 增加了不必要的开销,最简单的解决方法是执行步骤 C,然后使用无开销运算符将数据移回

【讨论】:

“不过,一班的开销太小了,可能甚至无法测量” 嗯?在一个紧密的循环中,它肯定会对性能产生影响。 ***.com/q/11227809/420683 例如比较 coliru.stacked-crooked.com/a/33db47995d378111 和 T == int。 (我猜这甚至不应该是分支错误预测效应。) @dyp:是的,它确实对性能有影响,但在大多数实际代码中,这种影响在噪音中消失了。在人为的情况下,它可以非常清楚地显示(如您展示的示例),但我相信受此严重影响的现实世界代码的数量可能很少。另一方面,我相信微优化确实会对实际代码产生严重的性能影响。 我同意这可能在很大程度上取决于实际程序和上下文(这几乎就是 Marc Glisse 所说的,我猜)。但是OP要求std::vector&lt;double&gt;,所以我认为对ints或doubles的大向量进行排序可能是一个合理的场景。 对于未选中的版本,与其重写自定义排序,不如包装迭代器更简单。

以上是关于std::optional<T> 的开销?的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 std::optional<T>::emplace 的第二个重载

是否有一种紧凑的方法可以使 std::optional<T>::value_or 对 T 的成员变量起作用

带有 std::any 和 std::optional 的 any_cast

为啥允许将 std::optional 与值进行比较? [复制]

如何在 if 语句中最好地测试和解包 std::optional

C++ 范围像 Java 流一样查找并返回 std::optional