2012-09-17 31 views
9

Một số nguồn trên Internets (cụ thể là this one) nói rằng std :: function sử dụng tối ưu hóa đóng cửa nhỏ, ví dụ: nó không phân bổ đống nếu kích thước đóng cửa thấp hơn một số lượng dữ liệu (link ở trên chỉ ra 16 byte cho gcc)g ++: std :: chức năng khởi tạo với kiểu đóng luôn sử dụng phân bổ đống?

Vì vậy, tôi đã đi đào bới g ++ header

Hình như có hay không tối ưu hóa như vậy được áp dụng quyết định bởi khối mã này trong "chức năng" tiêu đề (g ++ 4.6.3)

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

và một số dòng xuống:

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)); } 
    }; 

ví dụ như nếu _Local_storage() là true_type, hơn theo vị trí mới được gọi, nếu không - thường xuyên mới

defintion của _Local_storage là folowing:

typedef integral_constant<bool, __stored_locally> _Local_storage; 

và __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)); 

và cuối cùng là: __is_location_invariant:

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

So. theo như tôi có thể nói, loại đóng cửa không phải là một con trỏ hay một con trỏ thành viên. Để xác minh rằng tôi thậm chí đã viết một chương trình thử nghiệm nhỏ:

#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; 
} 

và đầu ra là:

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

Vì vậy, câu hỏi của tôi là như sau: được intializing std :: chức năng với lambda luôn kết quả với đống phân bổ? hoặc tôi đang thiếu một cái gì đó?

+0

Tôi xác nhận phát hiện của bạn bởi chương trình này: http://ideone.com/kzae6U Bạn có thể kiểm tra trên kêu vang (http: // melpon.org/wandbox/) rằng cùng một chương trình chỉ phân bổ bộ nhớ cho việc chụp rất lớn ... – PiotrNycz

Trả lời

1

std :: phân bổ chức năng là chi tiết triển khai; tuy nhiên cuối cùng tôi đã kiểm tra, 12 byte là kích thước functor tối đa cho msvc, 16 cho gcc, 24 cho tăng + msvc.

+0

adzm: vâng, điều này được đề cập trong liên kết ở đầu câu hỏi. Tuy nhiên, tôi không thấy điều này thực sự là trường hợp với g + + –

2

Tôi đặt cược nếu bạn thêm này:

std::cout << "std::__is_location_invariant: " << std::__is_location_invariant<lambda_t>::value << std::endl; 

bạn sẽ nhận lại:

std::__is_location_invariant: 0 

Ít nhất đó là những gì ideone says.

+0

Có, đó là khá nhiều ngụ ý từ thử nghiệm của tôi. Câu hỏi là: đây là cuối cùng? Tiến sĩ Dobbs là sai và chúng tôi luôn luôn có phân bổ đống? –

+0

@AlexI., Nó thực sự phụ thuộc vào trình biên dịch. – MSN

7

Theo GCC 4.8.1, hàm std :: trong libstdC++ chỉ tối ưu hóa cho con trỏ đến hàm và phương thức. Vì vậy, bất kể kích thước của functor của bạn (lambdas bao gồm), khởi tạo một std :: chức năng từ nó gây ra phân bổ đống. Thật không may là không có hỗ trợ cho phân bổ tùy chỉnh hoặc.

Visual C++ 2012 và LLVM libC++ tránh phân bổ cho bất kỳ functor đủ nhỏ nào.

Lưu ý, để tối ưu hóa này khởi động hàm functor của bạn nên hoàn thành tiêu chuẩn :: is_nothrow_move_constructible. Điều này là để hỗ trợ noexcept std :: function :: swap(). May mắn thay, lambdas thỏa mãn yêu cầu này nếu tất cả các giá trị đã capture thực hiện.

Bạn có thể viết một chương trình đơn giản để kiểm tra hành vi trên trình biên dịch khác nhau:

#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; 
} 
Các vấn đề liên quan