如果以递归方式使用 std::bind 是否有调用开销?

问题描述

我正在编写一个涉及 f = std::bind(std::bind(std::bind(...))) 使用的模板函数

但我不确定 c++ 编译器是否足够智能以展开调用链。
我的意思是:

  1. 在创建函子 f 时,是否有多个 std::bind() 在运行时调用
  2. 调用 f() 时,是否涉及调用 在 bind_functor 对象的不同层有多个 operator()

举个简单的例子:f2() 的运行速度是否比 f1() 稍快?

#include <functional>

int add(int a,int b) {
  return a + b;
}

int main() {
  using namespace std::placeholders;
  auto f1 = std::bind(std::bind(&add,1,_1),2);
  auto f2 = std::bind(&add,2);
  return 0;
}

更新: 我做了一些实验。看来 f2() 确实比 f1() 运行得更快。如果你使用 std::function,它会更慢。这是实验代码(ubuntu/gcc 7.5.0,启用了优化。没有优化,f2 是最慢的。)。在我的电脑上,输出是:

f1: 16851813
f2: 17567904
f3: 30655284

这是代码(根据 Nate 的评论更新):

#include <chrono>
#include <iostream>
#include <functional>

int add(int a,int b,int c) {
  return a + b + c;
}

int main() {
  using namespace std::placeholders;
  auto f1 = std::bind(std::bind(&add,_1,_2),2,_1);
  auto f2 = std::bind(&add,_1);
  std::function<int(int)> f3 = std::bind(&add,_1);
  const int N = 10000000;
  volatile int x = 0;

  {
    auto begin = std::chrono::system_clock::Now();
    for (int n = 0; n < N; n++) {
      x = f1(x);
    }   
    auto end = std::chrono::system_clock::Now();
    auto d = end - begin;
    std::cout << "f1: " << d.count() << std::endl;
  }

  x = 0;
  {
    auto begin = std::chrono::system_clock::Now();
    for (int n = 0; n < N; n++) {
      x = f2(x);
    }   
    auto end = std::chrono::system_clock::Now();
    auto d = end - begin;
    std::cout << "f2: " << d.count() << std::endl;
  }

  x = 0;
  {
    auto begin = std::chrono::system_clock::Now();
    for (int n = 0; n < N; n++) {
      x = f3(x);
    }   
    auto end = std::chrono::system_clock::Now();
    auto d = end - begin;
    std::cout << "f3: " << d.count() << std::endl;
  }
  return 0;
}

解决方法

鉴于 import sys import os base_dir = os.path.dirname(__file__) or '.' # Insert the package_dir_a directory at the front of the path. package_dir_a = os.path.join(base_dir,'package_dir_a') sys.path.insert(0,package_dir_a) import py_file 使用类型擦除,因此应该对 std::function 执行等效于虚拟调用的代码,operator() 的结果可以知道真实类型并使用静态调用(这更简单由编译器内联)。

嵌套的 bind 会执行额外的静态调用,但可能是内联的,因此可能会给出相同的代码。

Quickbench Demo 确认您的示例:

嵌套或非嵌套 bind 的时间相同,但 bind 慢。