boost::bind会导致性能开销吗?

5

我目前正在开发网络软件。它有一个主要的类server,显然表示一个服务器实例。

server 实例可以发送请求,并通过回调通知用户响应信息。

代码如下:

class server
{
  public:
    typedef boost::function<void (int duration)> callback_func;

    void send_request(endpoint& ep, callback_func cb);
};

现在假设我作为用户想要回调函数知道调用它的实例,我可以采取以下措施:

void mycallback(const server& sv, int duration) { ... }

server sv;
sv.send_request("localhost", boost::bind(&mycallback, boost::ref(sv), _1));

但我想知道:这样做是否会有任何开销?调用mycallback是否比使用“常规”调用更慢?
谢谢。
注:我当然可以将我的typedef更改为:typedef boost::function<void (const server& sv, int duration)> callback_func;,如果boost::bind造成了任何显著的开销,那么最终我可能会这样做。我只是想知道使用boost::bind意味着什么成本。
4个回答

5
当然它会增加额外开销。它所做的是创建一个存储绑定参数并具有 operator() 的函数对象,您可以使用其余参数调用该函数。现在,这是否重要?我不知道,因为那只是个词。发出一千万个请求并测量它。只有您才能确定这种开销对您是否重要。
此外,我面临了类似的问题。由于我真的不需要委托,所以我可以使用函数指针。但是我发现了一个有趣的基准测试,其中提到了一些替代实现,所有这些实现的性能都比boost :: function更好。这是以可移植性为代价,在某些情况下可能会使用非标准的丑陋技巧(但换来真正的优异性能)。

3
Boost v1.36.0 (于2008年发布)为 boost::function 引入了一种小对象优化,这使得 2007 年参考基准测试的结果无效。 - Marc Mutz - mmutz
那是一个重要的注释,谢谢。你知道最近有没有更多的基准测试吗? - Tamás Szelei

5

boost::bind 生成一个函数对象,如果将其用作函数模板参数,则可以进行优化;但是,boost::function 防止此优化,就像传递函数指针会阻止其内联一样。 boost::function 本身不需要引入比虚函数更多的开销,也不需要通过函数指针进行调用。

附注:我同意Tamás Szelei的观点:发出1000万个请求并测量它。


即使作为模板函数的参数传递,它仍取决于您的编译器:前几天我正在测试使用迭代器的直接循环与使用boost::bind的std::for_each,gcc 4.5将其优化为几乎相同的代码,而gcc 4.1和4.2则产生了相当大的膨胀。 - Alex B
boost::function<> 对于不是非常小的对象使用堆分配。因此,最好检查您的绑定对象是否低于限制。 - Johannes Schaub - litb

2
与普通函数调用相比,您需要支付两次间接寻址来进行函数调用,这类似于(未经虚拟化的)虚函数调用的开销。
第一次间接寻址是由于在boost::function中发生的类型擦除造成的(这在boost::function文档中有记录)。这个不太可能被优化,如果使用裸函数指针也会有同样的惩罚。
第二次间接寻址来自通过函数指针调用mycallback函数。非常优化的编译器可以找出并将其优化掉,但普通编译器则不行。如果将mycallback转换为函数对象,则可以消除所有编译器中的此间接寻址:
void mycallback( .1. ) { .2. }

你做。
struct mycallback {
    void operator()( .1. ) const { .2. }
};

1
请注意boost::bind会导致仿函数的复制。这可能非常重要。对于我的操作系统,new和delete的开销非常昂贵。请参阅boost::bind文档关于ref()和cref()的内容。我认为成员函数也会导致仿函数的复制,但是没有关于此问题的文档说明。

2
我错了。boost::bind不会复制函数对象,但它确实会复制参数。请参阅boost::bind文档中的ref()和cref()版本,以获取不复制参数的版本。 - Ant

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接