限制在C ++中内联函数包装器

时间:2012-09-29 16:33:22

标签: c++ optimization c++11 inlining

我的问题涉及在C ++中对函数包装器进行内联优化的应用,考虑以下代码,WorkerStructure对象使用函数包装器初始化,该函数包装器封装了一些功能块。然后在调用WorkerStructure :: doSomeWork方法时使用函数包装器。

当应用于WorkerStructure :: doSomeWork方法时,workerFunction对象封装的功能是否会内联?显然,如果在其他某个转换单元中定义了该功能,则workerFunction对象只封装一个函数指针,是否存在任何其他无法内联的情况?

当通过函数包装器传递在不同转换单元中定义的lambda函数时,它是否实际上等同于传递函数指针?

struct WorkerStructure
{
    WorkerStructure(std::function <bool(float)> &f):workerFunction(f) {}

    void doSomeWork(float inputValue)
    {
        if(workerFunction(inputValue))
        {
            //do some conditional operation
        }
    }
    std::function <bool(float)> workerFunction ;
};

2 个答案:

答案 0 :(得分:5)

std::function的多态特性固有地使非常难以实际内联调用。因为std::function可以讲述任何可调用的实体;你会如何编写内联代码?

这有点像内联通过基指针调用的虚函数,没有其他可用信息(也就是在调用之前没有从派生到基指针的赋值,编译器可能会使用它来启用内联)。

大多数情况下,std::function使用void*指针和一个指向模板化函数的特化的函数指针来实现,它可以执行实际的调用和转换。当然有变体使用虚函数来做到这一点,并且它们更清楚,为什么它非常难以实现。即使链接时优化也无法做任何事情,因为无关紧要,你已经拥有了你在呼叫站点可以获得的所有信息(这并不多)。

这是非常原始版本的std::function使用模板函数版本的指针,只处理存储和调用方面(省略内存管理,复制,移动,重置,空间优化等。):

template<class Sig>
class function;

template<class R, class... Args>
class function<R(Args...)>{
  typedef R (*call_type)(void*, Args...);
  void* _obj;
  call_type _caller;

public:
  template<class F>
  function(F f)
    : _obj(new F(f))
    , _caller([](void* p, Args... args){ return (*static_cast<F*>(p))(args...); })
  {}

  R operator()(Args... args) const{
    return _caller(_obj, args...);
  }
};

Live example.我认为很难检查_obj_caller内部的内容以及调用function的位置。

仅供参考,here's the version with virtual functions

答案 1 :(得分:1)

有趣的是,我今天在the mailing list询问了关于Clang / LLVM中虚拟功能的内联。 std::function的动态特性使其基本上成为虚拟调用,因为virtual调用不仅仅是指向函数的指针。

以LLVM为例,让我们使用以下程序:

#include <cstdio>

typedef void (*Function)();

void donothing() {}
void print() { printf("Hello World!"); }

Function get(int i) {
    if (i % 2 == 0) { return donothing; }
    return print;
}

int main() {
    Function f = get(0);
    f();
}

发出的主要功能:

define i32 @main() uwtable readnone {
  ret i32 0
}

因此,编译器能够理解选择哪个函数(结合内联和常量传播),并内联调用。

不幸的是,我在我的电子邮件中证明了通过虚拟表这不起作用(优化器以某种方式丢失了信息并且无法内联调用)。因此尽管内联完全可以通过std::function工作,但它很可能不仅取决于编译器,而且还取决于您恰好使用的std::function的特定实现。我担心你需要试验你的申请。

相关问题