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<double>
相比,std::vector<std::experimental::optional<double>>
上的操作开销的数量级是多少?
【问题讨论】:
你需要更精确的测试用例才能判断。开销可以从可以忽略不计到 10 倍(防止矢量化和其他优化)。但是你不会用同样的方式使用它们,一个有额外的功能,所以它是比较苹果和橘子。 【参考方案1】:除了其他答案之外,您还应该考虑std::optional
需要额外的内存。
通常它不仅仅是一个额外的字节,而是(至少对于“小”类型)由于填充而导致的 2x 空间开销。
也许 RAM 不是问题,但这也意味着缓存中可用的值更少。
如果特定知识允许使用哨兵值,可能是更好的选择(可能以markable
的形式保持类型安全)。
一个有趣的阅读是:Boost optional - Performance considerations
【讨论】:
【参考方案2】:->
和 *
应该有零开销。value
和 value_or
应该有一个分支的开销:if(active)
此外,复制/移动构造函数、复制/移动赋值、swap、emplace、operator==
、operator<
和析构函数也应该有一个分支的开销。
然而,一组开销是如此之小,以至于可能甚至无法测量。说真的,写漂亮的代码,不要担心这里的性能。使代码漂亮的可能性会导致它运行得比你试图让它更快的速度更快。违反直觉,但无论如何都要这样做。
在某些情况下开销变得明显,例如对大量optional
s 进行排序。在这些情况下,有四种情况,
(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<double>
,所以我认为对int
s或double
s的大向量进行排序可能是一个合理的场景。
对于未选中的版本,与其重写自定义排序,不如包装迭代器更简单。以上是关于std::optional<T> 的开销?的主要内容,如果未能解决你的问题,请参考以下文章
如何使用 std::optional<T>::emplace 的第二个重载
是否有一种紧凑的方法可以使 std::optional<T>::value_or 对 T 的成员变量起作用
带有 std::any 和 std::optional 的 any_cast
为啥允许将 std::optional 与值进行比较? [复制]