g ++:std :: function用closure类型初始化总是使用堆分配? [英] g++: std::function initialized with closure type always uses heap allocation?

查看:151
本文介绍了g ++:std :: function用closure类型初始化总是使用堆分配?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

Internets上的一些来源(特别是这一个)说std :: function使用小闭包优化,例如它不分配堆如果闭包大小低于一定量的数据(上面的链接表示16字节为gcc)



所以我去挖掘通过g ++头



看起来是否应用这种优化是由功能标题(g ++ 4.6.3)中的这段代码决定的

  static void 
_M_init_functor(_Any_data& __functor,_Functor& __f)
{_M_init_functor(__ functor,std :: move(__ f),_Local_storage ); }

和一些行:

  static void 
_M_init_functor(_Any_data& __functor,_Functor& __f,true_type)
{new(__functor._M_access())_Functor(std :: move )); }

static void
_M_init_functor(_Any_data& __functor,_Functor& __f,false_type)
{__functor._M_access< _Functor *>()= new _Functor(std: :move(__ f)); }
};

例如,如果_Local_storage()是true_type, p>

定义_Local_storage如下:

  typedef integral_constant< bool,__stored_locally> ; _Local_storage; 

和__stored_locally:

  static const std :: size_t _M_max_size = sizeof(_Nocopy_types); 
static const std :: size_t _M_max_align = __alignof __(_ Nocopy_types);

static const bool __stored_locally =
(__is_location_invariant< _Functor> :: value
&&&&&&&&& sizeof(_Functor)< = _M_max_size
& __alignof__ (_Functor)< = _M_max_align
&&(_M_max_align%__alignof __(_ Functor)== 0));

,最后:__is_location_invariant:

  template< typename _Tp> 
struct __is_location_invariant
:integral_constant< bool,(is_pointer< _Tp> :: value
|| is_member_pointer< _Tp> :: value)>
{};

只要我能告诉,闭包类型既不是指针也不是成员指针。要验证我是否写了一个小测试程序:

  #include< functional> 
#include< iostream>

int main(int argc,char * argv [])
{
std :: cout< max stored local size:< sizeof(std :: _Nocopy_types)<< ,align:< __alignof __(std :: _ Nocopy_types)<< std :: endl;

auto lambda = [](){};

typedef decltype(lambda)lambda_t;

std :: cout<< lambda size:<< sizeof(lambda_t)< std :: endl;
std :: cout<< lambda align:< __alignof __(lambda_t)<< std :: endl;

std :: cout<< 本地存储:< ((std :: __ is_location_invariant< lambda_t> :: value
&&&&&&&&&&&&&&&&&& amp; _of _ size :: _ Function_base :: _ M_max_align
&(std :: _ Function_base :: _ M_max_align%__alignof __(lambda_t)== 0))?true:false)& std :: endl;
}

,输出为:


$ b b

  max stored local size:16,align:8 
lambda size:1
lambda align:1
本地存储:false $ b $所以,我的问题是以下:是intializing std :: function与lambda总是结果与堆分配?

解决方案

从GCC 4.8.1开始,libstdc ++中的std ::函数只优化指针功能和方法。因此,不管你的函子的大小(包括lambdas),从它初始化一个std ::函数触发堆分配。不幸的是,也不支持自定义分配器。



Visual C ++ 2012和LLVM libc ++避免分配任何足够小的函数。



注意,对于这种优化,你的函数应该满足std :: is_nothrow_move_constructible。这是为了支持noexcept std :: function :: swap()。幸运的是,如果所有捕获的值都满足,lambdas就满足这个要求。



你可以写一个简单的程序来检查各种编译器的行为:

  #include< functional> 
#include< iostream>

// MSVC11中缺少noexpect
#ifdef _MSC_VER
#define NOEXCEPT
#else
#define NOEXCEPT noexcept
#endif

struct A
{
A(){}
A(const A&){}
A(&&&& other)NOEXCEPT {std: :cout< A(A&&)\\\
; }

void operator()()const {std :: cout< 一个; }

char data [FUNCTOR_SIZE];
};

int main()
{
std :: function< void()> f((A()));
f();

//如果使用小函数优化,打印A(A&& amp;)
auto f2 = std :: move(f);

return 0;
}


Some sources on the Internets (specifically this one) says that std::function use small-closure optimizations, e.g. it do not allocate heap if closure size is lower than some amount of data (link above indicates 16 bytes for gcc)

So I went digging through g++ headers

Looks like whether or not such optimization is applied is decided by this block of code in "functional" header (g++ 4.6.3)

static void
_M_init_functor(_Any_data& __functor, _Functor&& __f)
{ _M_init_functor(__functor, std::move(__f), _Local_storage()); }

and some lines down:

static void
_M_init_functor(_Any_data& __functor, _Functor&& __f, true_type)
{ new (__functor._M_access()) _Functor(std::move(__f)); }

static void
_M_init_functor(_Any_data& __functor, _Functor&& __f, false_type)
{ __functor._M_access<_Functor*>() = new _Functor(std::move(__f)); }
  };

e.g if _Local_storage() is true_type, than placement-new is called, otherwise - regular new

defintion of _Local_storage is the folowing:

typedef integral_constant<bool, __stored_locally> _Local_storage;

and __stored_locally:

static const std::size_t _M_max_size = sizeof(_Nocopy_types);
static const std::size_t _M_max_align = __alignof__(_Nocopy_types);

static const bool __stored_locally =
(__is_location_invariant<_Functor>::value
 && sizeof(_Functor) <= _M_max_size
 && __alignof__(_Functor) <= _M_max_align
 && (_M_max_align % __alignof__(_Functor) == 0));

and finally: __is_location_invariant:

template<typename _Tp>
struct __is_location_invariant
: integral_constant<bool, (is_pointer<_Tp>::value
               || is_member_pointer<_Tp>::value)>
{ };

So. as far as I can tell, closure type is neither a pointer nor a member pointer. To verify that I even wrote a small test program:

#include <functional>
#include <iostream>

int main(int argc, char* argv[])
{
  std::cout << "max stored locally size: " << sizeof(std::_Nocopy_types) << ", align: " << __alignof__(std::_Nocopy_types) << std::endl;

  auto lambda = [](){};

  typedef decltype(lambda) lambda_t;

  std::cout << "lambda size: " << sizeof(lambda_t) << std::endl;
  std::cout << "lambda align: " << __alignof__(lambda_t) << std::endl;

  std::cout << "stored locally: " << ((std::__is_location_invariant<lambda_t>::value
     && sizeof(lambda_t) <= std::_Function_base::_M_max_size
     && __alignof__(lambda_t) <= std::_Function_base::_M_max_align
     && (std::_Function_base::_M_max_align % __alignof__(lambda_t) == 0)) ? "true" : "false") << std::endl;
}

and the output is:

max stored locally size: 16, align: 8
lambda size: 1
lambda align: 1
stored locally: false

So, my questions is the following: is intializing std::function with lambda always results with heap allocation? or am I missing something?

解决方案

As of GCC 4.8.1, the std::function in libstdc++ optimizes only for pointers to functions and methods. So regardless the size of your functor (lambdas included), initializing a std::function from it triggers heap allocation. Unfortunately there is no support for custom allocators either.

Visual C++ 2012 and LLVM libc++ do avoid allocation for any sufficiently small functor.

Note, for this optimization to kick in your functor should fulfill std::is_nothrow_move_constructible. This is to support noexcept std::function::swap(). Fortunately, lambdas satisfy this requirement if all captured values do.

You can write a simple program to check behavior on various compilers:

#include <functional>
#include <iostream>

// noexpect missing in MSVC11
#ifdef _MSC_VER
# define NOEXCEPT
#else
# define NOEXCEPT noexcept
#endif

struct A
{
    A() { }
    A(const A&) { }
    A(A&& other) NOEXCEPT { std::cout << "A(A&&)\n"; }

    void operator()() const { std::cout << "A()\n"; }

    char data[FUNCTOR_SIZE];
};

int main()
{
    std::function<void ()> f((A()));
    f();

    // prints "A(A&&)" if small functor optimization employed
    auto f2 = std::move(f); 

    return 0;
}

这篇关于g ++:std :: function用closure类型初始化总是使用堆分配?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆