原文转自:http://www.tanjp.com (即时修正和更新)
固定大小仿函数(FixedFunction)
仿函数是C++很多新特性的基础,如 lambda表达式。但是STL提供的std::function兼顾了太多情况,性能不高。于是做了优化,实现代码如下:
template <typename SIGNATURE, size_t STORAGE_SIZE = 128>
class FixedFunction;
template <typename R, typename... ARGS, size_t STORAGE_SIZE>
class FixedFunction<R(ARGS...), STORAGE_SIZE>
{
typedef R (*FuncPtrType)(ARGS...);
typedef R(*MethodType)(void* object_ptr, FuncPtrType free_func_ptr, ARGS... args);
typedef void(*AllocType)(void* storage_ptr, void* object_ptr);
public:
FixedFunction() : mf_function_ptr(nullptr), mf_method_ptr(nullptr), mf_alloc_ptr(nullptr) {}
template <typename FUNC>
FixedFunction(FUNC&& object) : FixedFunction()
{
typedef typename std::remove_reference<FUNC>::type unref_type;
static_assert(sizeof(unref_type) < STORAGE_SIZE, "functional object doesn't fit into internal storage");
static_assert(std::is_move_constructible<unref_type>::value, "Should be of movable type");
mf_method_ptr = [](void* object_ptr, FuncPtrType, ARGS... args) -> R
{
return static_cast<unref_type*>(object_ptr)->operator()(args...);
};
mf_alloc_ptr = [](void* storage_ptr, void* object_ptr)
{
if(object_ptr)
{
unref_type* x_object = static_cast<unref_type*>(object_ptr);
new(storage_ptr) unref_type(std::move(*x_object));
}
else
{
static_cast<unref_type*>(storage_ptr)->~unref_type();
}
};
mf_alloc_ptr(&ms_storage, &object);
}
template <typename RET, typename... PARAMS>
FixedFunction(RET (*func_ptr)(PARAMS...)) : FixedFunction()
{
mf_function_ptr = func_ptr;
mf_method_ptr = [](void*, FuncPtrType f_ptr, ARGS... args) -> R
{
return static_cast<RET (*)(PARAMS...)>(f_ptr)(args...);
};
}
FixedFunction(FixedFunction&& o) : FixedFunction()
{
move_from_other(o);
}
FixedFunction& operator=(FixedFunction&& o)
{
move_from_other(o);
return *this;
}
~FixedFunction()
{
if(mf_alloc_ptr) mf_alloc_ptr(&ms_storage, nullptr);
}
R operator()(ARGS... args)
{
if(!mf_method_ptr) throw std::runtime_error("call of empty functor");
return mf_method_ptr(&ms_storage, mf_function_ptr, args...);
}
private:
// 不可拷贝
FixedFunction& operator=(const FixedFunction&) = delete;
FixedFunction(const FixedFunction&) = delete;
void move_from_other(FixedFunction& o)
{
if(this == &o) return;
if(mf_alloc_ptr)
{
mf_alloc_ptr(&ms_storage, nullptr);
mf_alloc_ptr = nullptr;
}
else
{
mf_function_ptr = nullptr;
}
mf_method_ptr = o.mf_method_ptr;
o.mf_method_ptr = nullptr;
if(o.mf_alloc_ptr)
{
mf_alloc_ptr = o.mf_alloc_ptr;
mf_alloc_ptr(&ms_storage, &o.ms_storage);
}
else
{
mf_function_ptr = o.mf_function_ptr;
}
}
private:
union
{
typename std::aligned_storage<STORAGE_SIZE, sizeof(size_t)>::type ms_storage;
FuncPtrType mf_function_ptr;
};
MethodType mf_method_ptr;
AllocType mf_alloc_ptr;
};
性能测试示例
void test1_func()
{
std::string s;
for (uint32 i = 0; i < 10; ++i)
{
s.append(1, (char)i);
}
s.reserve();
}
// 函数直接调用
for (uint32 i = 0; i < 2000000; ++i)
{
test_func();
}
// std::function包装并调用
std::function<void()> f;
for (uint32 i = 0; i < 2000000; ++i)
{
f = std::move(std::bind(&test_func));
f();
}
// FixedFunction包装并调用
FixedFunction<void(), 128> ff;
for (uint32 i = 0; i < 2000000; ++i)
{
test_func();
ff = std::move(std::bind(&test_func));
ff();
}
结果
执行 函数2000000次
C++函数直接调用耗时(毫秒) :12032
std::function包装调用耗时(毫秒) : 20632
FixedFunction包装调用耗时(毫秒):13442
优化效果明显,FixedFunction调用的性能接近C++直接函数调用。