2013-04-11 26 views

Trả lời

18

Thứ nhất, chúng ta nên biết ý tưởng cơ bản đằng sau thư viện Boost Hồ bơi: simple_segregated_storage, nó cũng tương tự như một danh sách đơn lẻ liên kết, và có trách nhiệm đối với phân vùng một khối nhớ vào khối kích thước cố định: enter image description here

Một nhóm bộ nhớ giữ một danh sách miễn phí của khối bộ nhớ. Vì vậy, chúng tôi đã đề cập đến khối và khối: nhóm bộ nhớ sử dụng new hoặc malloc để cấp phát một khối bộ nhớ và chia thành nhiều khối bộ nhớ có cùng kích thước.
Giả sử địa chỉ được căn chỉnh 8, 4 byte để lưu địa chỉ của đoạn tiếp theo, do đó khối bộ nhớ (8 byte * 32 khối) như sau (địa chỉ bộ nhớ chỉ để minh họa câu hỏi chứ không phải là câu hỏi thực) :
a memory block

Bây giờ, giả sử người dùng phân bổ bộ nhớ 8 byte hai lần, do đó, các khối: [0xDD00,0xDD08), [0xDD08,0xDD10) được sử dụng. Sau một thời gian, người dùng phát hành bộ nhớ tại [0xDD00,0xDD08), vì vậy đoạn này sẽ quay trở lại danh sách miễn phí. Bây giờ khối là như thế này:

enter image description here
Sau đó người dùng nhả bộ nhớ tại địa chỉ [0xDD08,0xDD10), cách đơn giản nhất để đặt đoạn này trở lại trong danh sách là để cập nhật các first để trỏ đến nó, thời gian liên tục phức tạp. các simple_segregated_storage<T>::free() được làm điều này một cách chính xác:

void free BOOST_PREVENT_MACRO_SUBSTITUTION(void * const chunk) 
{ //! Free a chunk. 
    //! \pre chunk was previously returned from a malloc() referring to the same free list. 
    //! \post !empty() 
    BOOST_POOL_VALIDATE_INTERNALS 
    nextof(chunk) = first; 
    first = chunk; 
    BOOST_POOL_VALIDATE_INTERNALS 
} 

Sau đó, danh sách sẽ là như thế này:
unordered list
Bây giờ chúng tôi nhận thấy danh sách các khối không sắp thứ tự theo địa chỉ của họ sau khi các hoạt động này! Nếu chúng tôi muốn giữ gìn trật tự trong khi không phân bổ, hãy gọi pool<>::ordered_free() thay vì pool<>::free() để đặt lại bộ nhớ trong danh sách theo đúng thứ tự của nó. Bây giờ chúng ta đã biết thứ tự trong hồ bơi bộ nhớ là gì, chúng ta hãy thâm nhập vào mã nguồn của boost::pool<>::mallocboost::pool<>::ordered_malloc:

void * malloc BOOST_PREVENT_MACRO_SUBSTITUTION() 
{ 
    if (!store().empty()) 
    return (store().malloc)(); 
    return malloc_need_resize(); 
} 

void * ordered_malloc() 
{ 
    if (!store().empty()) 
    return (store().malloc)(); 
    return ordered_malloc_need_resize(); 
} 

Như chúng ta có thể thấy, họ chỉ khác nhau khi không có đoạn tự do trong danh sách các bộ nhớ khối. Trong trường hợp này, nó phân bổ một khối bộ nhớ mới, kết hợp danh sách miễn phí của nó vào danh sách miễn phí của nhóm, sự khác biệt giữa hai phương pháp này là boost::pool<>::ordered_malloc duy trì thứ tự trong khi hợp nhất các danh sách miễn phí.
Ở trên là dành cho câu hỏi 1.
Vì vậy, tại sao đơn đặt hàng lại quan trọng ?! Có vẻ như các hồ bơi bộ nhớ hoạt động hoàn hảo với các khối không theo thứ tự!
Đầu tiên, nếu chúng ta muốn tìm một chuỗi liên tiếp của các khối n, danh sách miễn phí được sắp xếp sẽ làm cho nó dễ dàng hơn.Thứ hai, Chúng ta hãy có một cái nhìn tại các lớp có nguồn gốc của boost::pool: boost::object_pool, nó cung cấp sự hủy diệt tự động của các đối tượng phi deallocated về sự hủy diệt của đối tượng object_pool trong khi bạn cũng có thể tiêu diệt các đối tượng bằng tay, ví dụ:

class X { … }; 

    void func() 
    { 
     boost::object_pool<X> alloc; 

     X* obj1 = alloc.construct(); 
     X* obj2 = alloc.construct(); 
     alloc.destroy(obj2); 
    } 

sự mã trên là OK, không bị rò rỉ bộ nhớ hoặc xóa đôi! Làm thế nào để boost::object_pool làm phép thuật này? Hãy tìm thi hành destructor của boost::object_pool (Tôi có tăng 1,48 trên máy tính của tôi):

template <typename T, typename UserAllocator> 
object_pool<T, UserAllocator>::~object_pool() 
{ 
#ifndef BOOST_POOL_VALGRIND 
    // handle trivial case of invalid list. 
    if (!this->list.valid()) 
    return; 

    details::PODptr<size_type> iter = this->list; 
    details::PODptr<size_type> next = iter; 

    // Start 'freed_iter' at beginning of free list 
    void * freed_iter = this->first; 

    const size_type partition_size = this->alloc_size(); 

    do 
    { 
    // increment next 
    next = next.next(); 

    // delete all contained objects that aren't freed. 

    // Iterate 'i' through all chunks in the memory block. 
    for (char * i = iter.begin(); i != iter.end(); i += partition_size) 
    { 
     // If this chunk is free, 
     if (i == freed_iter) 
     { 
     // Increment freed_iter to point to next in free list. 
     freed_iter = nextof(freed_iter); 

     // Continue searching chunks in the memory block. 
     continue; 
     } 

     // This chunk is not free (allocated), so call its destructor, 
     static_cast<T *>(static_cast<void *>(i))->~T(); 
     // and continue searching chunks in the memory block. 
    } 

    // free storage. 
    (UserAllocator::free)(iter.begin()); 

    // increment iter. 
    iter = next; 
    } while (iter.valid()); 

    // Make the block list empty so that the inherited destructor doesn't try to 
    // free it again. 
    this->list.invalidate(); 
#else 
    // destruct all used elements: 
    for(std::set<void*>::iterator pos = this->used_list.begin(); pos != this->used_list.end(); ++pos) 
    { 
     static_cast<T*>(*pos)->~T(); 
    } 
    // base class will actually free the memory... 
#endif 
} 

nó đi qua tất cả các khối trong danh sách các khối bộ nhớ (list, các thành viên dữ liệu boost::pool<>, nắm giữ những vị trí và kích thước của tất cả các khối bộ nhớ được cấp phát từ hệ thống) để tìm xem có đoạn nào trong đó cũng hiển thị trong danh sách miễn phí hay không, nếu không, gọi hàm hủy của đối tượng, sau đó giải phóng bộ nhớ. Vì vậy, nó là loại nhận được giao điểm của hai bộ, giống như std::set_intersection() không! Nếu danh sách được sắp xếp, nó sẽ nhanh hơn nhiều để làm điều đó. Trên thực tế trong boost::object_pool<>, thứ tự là cần thiết, các hàm thành viên công cộng: boost::object_pool<>::malloc()boost::object_pool<>::free() chỉ cần gọi boost::pool<>::ordered_malloc()boost::pool<>::ordered_free() tương ứng:

element_type * malloc BOOST_PREVENT_MACRO_SUBSTITUTION() 
{ //! Allocates memory that can hold one object of type ElementType. 
    //! 
    //! If out of memory, returns 0. 
    //! 
    //! Amortized O(1). 
    return static_cast<element_type *>(store().ordered_malloc()); 
} 
void free BOOST_PREVENT_MACRO_SUBSTITUTION(element_type * const chunk) 
{ //! De-Allocates memory that holds a chunk of type ElementType. 
    //! 
    //! Note that p may not be 0.\n 
    //! 
    //! Note that the destructor for p is not called. O(N). 
    store().ordered_free(chunk); 
} 

Vì vậy, cho queston 2: Bạn không cần phải sử dụng boost::pool<>::ordered_malloc trong hầu hết các tình huống.

+2

câu trả lời tuyệt vời! –

Các vấn đề liên quan