【问题标题】:Performance of boost::mp11::mp_with_index compared to array of std::functionboost::mp11::mp_with_index 与 std::function 数组相比的性能
【发布时间】:2021-12-27 11:14:10
【问题描述】:

考虑以下两个sn-ps:

// Option (1).
boost::mp11::mp_with_index<N>(i,
                              [&](const auto i){ function<i>(/* args... */); });

// Option (2).
inline static const std::array<std::function<void(/* Args... */)>, N>
    functionArray{function<0>, ..., function<N-1>};
functionArray[i](/* args... */);

其中N 是大约在[0, 20] 范围内的编译时间大小,i0N-1 之间的运行时索引,template &lt;size_t I&gt; function(/* Args... */) 是具有已知签名的模板函数. 这两个选项中哪一个是最快的?

注意:我知道boost::mp11::mp_with_index 基本上创建了一个 switch 语句,允许将运行时索引转换为编译时索引。这引入了一些间接性,但我希望这不会太昂贵。同样,我知道std::function 由于类型擦除而引入了一些间接性。我的问题是:这两种间接方式中哪一种最有效?

【问题讨论】:

  • 了解的唯一方法是在实际代码中测量它。您的代码示例中的std::function 在哪里?
  • @Evg 感谢您指出,我刚刚编辑了我的问题!我想知道std::function 实现的那种间接性是否与boost::mp11::mp_with_index 提供的一样有效。对我来说,这看起来几乎不可能,因为std::function 不知道N,所以它不能创建一个带有签名void(/* Args... */) 的所有可能实现的编译时间表。有谁知道std::function 在后台是如何工作的?

标签: c++ boost callback benchmarking std-function


【解决方案1】:

std::array&lt;std::function&lt;void(Args...)&gt;, N&gt; 与纯指针数组std::array&lt;void(*)(Args...), N&gt; 相比可能会引入一些开销。

查看https://godbolt.org/z/a8z9aKs7P处生成的程序集,可以进行以下观察:

boost::mp11::mp_with_index 被编译为一个分支表,其中包含 N 个不同指令的 N 个地址,这些指令在跳转到时只需调用 function&lt;I&gt;。所以,它会在分支表中寻找一个地址,跳转到那个地址,然后再次跳转到想要的函数。

这个分支表可以通过简单地存储function&lt;I&gt;的地址来简化,只需要一次跳转。这当你有一个函数指针数组时发生的事情,这个数组本质上是一个分支表。

std::function 类似,但调用std::function 比调用常规函数指针稍微复杂一些。


注意,clang 在 -O2 甚至 -O3 上优化这个很糟糕。 boost::mp11::mp_with_index&lt;N&gt; 实际上是一堆 if/else 语句,它们应该很容易编译,就好像它是 switch 一样,但是 clang 无法做到这一点(并且留下了 N “比较和条件跳转”指令)。函数指针数组是这里唯一的好选择。

【讨论】:

  • 感谢您的精彩回答!换句话说,最有效的选择是函数指针数组!但是如果我感兴趣的函数是异构类的成员函数呢?在这种情况下,它们不能存储在函数指针中。在这种情况下,最好的选择是什么?
  • 可以肯定地说,在异构类的成员函数的一般情况下(不能存储在函数指针中),那么最好的选择是将它们存储在std::function的数组中与 Boost 方法相比?为什么会这样?事实上,正如我在对主要问题的评论中指出的那样,std::functionBoost 方法相比使用的信息更少。
  • @fdev 您可以使用指向成员函数的指针,或者将它们包装在可以转换为成员函数的 lambda 中。 Boost 方法生成的代码略少(std::function 的静态初始化程序和sizeof(std::function&lt;void(Args...)&gt; == 32 有代码,因此实际存储数组需要空间,而mp_with_index 只生成一对跳转),但可能会有类似的对 std::function 数组的性能,您必须进行基准测试以确保。
猜你喜欢
  • 1970-01-01
  • 2012-12-27
  • 2012-04-17
  • 2011-04-07
  • 2015-07-27
  • 1970-01-01
  • 2010-09-19
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多