【问题标题】:Lambda to std::function conversion performanceLambda 到 std::function 的转换性能
【发布时间】:2011-11-28 16:16:13
【问题描述】:

我想使用 lambda 函数异步调用引用计数对象上的方法:

void RunAsync(const std::function<void()>& f) { /* ... */ }

SmartPtr<T> objPtr = ...
RunAsync([objPtr] { objPtr->Method(); });

创建 lambda 表达式显然会创建一个副本,但我现在遇到的问题是将 lambda 表达式转换为 std::function 对象也会创建一堆我的智能指针的副本,并且每个副本都会增加引用计数。

以下代码应演示此行为:

#include <functional>

struct C {
    C() {}
    C(const C& c) { ++s_copies; }

    void CallMe() const {}

    static int s_copies;
};

int C::s_copies = 0;

void Apply(const std::function<void()>& fct) { fct(); }

int main() {
    C c;
    std::function<void()> f0 = [c] { c.CallMe(); };
    Apply(f0);
    // s_copies = 4
}

虽然之后引用量恢复正常,但出于性能原因,我想防止过多的引用操作。我不确定所有这些复制操作来自哪里。

有没有什么方法可以通过减少我的智能指针对象的副本来实现这一点?

更新:编译器是 Visual Studio 2010。

【问题讨论】:

  • 为什么不通过引用捕获?如[&amp;c] { c.CallMe(); }; 和智能指针一样。 `
  • @Dani:在许多引用计数的智能指针中,算法是原子的,因此需要更多的开销。
  • @Nawaz 嗯,我不确定这是否可能。如果仅通过引用捕获变量超出范围时会发生什么?
  • 您是否考虑过使用函数模板以便完全避开std::function?如果您可以使您的 lambda 无捕获(即,如果您可以找到其他方法将 C 放入 lambda,例如通过使用参数),您可以使用函数指针。
  • 您使用的是哪个编译器? MSVC2010 没有属性实现默认移动构造函数,因此您的 lambda 只能复制。我怀疑如果您使用移动语义手动实现 lambda,您会看到您想要的(这就是 C++11 的正确实现方式)。

标签: c++ c++11


【解决方案1】:

std::function 可能不会像自定义函子那样快,直到编译器对简单的情况实施一些严肃的特殊处理。

但是当move 合适时,引用计数问题是复制的症状。正如其他人在 cmets 中指出的那样,MSVC 没有正确实现 move。你描述的用法只需要移动,不需要复制,所以永远不要碰引用计数。

如果可以,请尝试使用 GCC 进行编译,看看问题是否消失。

【讨论】:

  • 不幸的是,我无法在 GCC 中编译整个项目,但我的演示代码在 GCC 中的行为符合预期。因此,如果我理解正确,问题不在于 lambda 表达式,而是std::function 实现?在使用 std::functionstd::bind 时,我似乎遇到了相同(或更糟)的问题。
  • 另外,我曾经有一个自定义仿函数实现,我想用std::function 替换它,但它不会显示这种行为。看来我还不能摆脱它。
  • @fschoenm 我不使用 Windows,但 James McNellis 在 Microsoft 工作,因此您可以在 cmets 中尝试他的建议。尝试将 std::function 替换为原始函数指针,因为 lambda 函数也会转换为该指针。
【解决方案2】:

转换为std::function应该只对 lambda 进行移动。如果这不是已经完成的,那么可以说在std::function 的实现或规范中存在错误。另外,在您上面的代码中,我只能看到原始c两个 副本,一个用于创建lambda,另一个用于从中创建std::function。我看不出多余的副本是从哪里来的。

【讨论】:

  • 我忘了提到我使用的是 Visual Studio 2010。在 GCC 中,我也只得到了两个副本(如果我实现了移动构造函数,则一个副本)。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2013-10-14
  • 2021-02-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多