2015-11-15 5 views
20

मैं हाल ही में हॉवर्ड हिन्नेंट के short_alloc में आया हूं और यह मैंने देखा है कि कस्टम आवंटकों का एकमात्र बेहतरीन उदाहरण है।हिन्नेंट की शॉर्ट_लोक और संरेखण गारंटी

लेकिन जैसा कि मैंने अपनी व्यक्तिगत परियोजना में इसे एकीकृत करने के लिए कोड का अध्ययन करने में और अधिक समय बिताया, यह मेरे लिए हुआ कि arena वर्ग, स्टैक-आधारित आवंटन प्रदान करने से, हमेशा ठीक से गठबंधन स्मृति नहीं लौटा सकता है। वास्तव में, मुझे डर है कि केवल पहले आवंटन उपयुक्त रूप से गठबंधन किया जाना (के रूप में अपने आप में एक बफर मजबूर संरेखण है), प्रासंगिक कोड के टुकड़े नीचे देखें गारंटी है:

template <std::size_t N> 
class arena 
{ 
    static const std::size_t alignment = 16; 
    alignas(alignment) char buf_[N]; 
    char* ptr_; 
    //... 
}; 

template <std::size_t N> 
char* 
arena<N>::allocate(std::size_t n) 
{ 
    assert(pointer_in_buffer(ptr_) && "short_alloc has outlived arena"); 
    if (buf_ + N - ptr_ >= n) 
    { 
    char* r = ptr_; 
    ptr_ += n; 
    return r; 
    } 
    return static_cast<char*>(::operator new(n)); 
} 

मैं इसे ठीक करने के लिए कुछ तरीके के बारे में सोच सकते हैं (कुछ मेमोरी बर्बादी की लागत पर) में alignment के एकाधिक में size को गोल करने का सबसे आसान तरीका है।

लेकिन कुछ भी बदलने से पहले, मुझे यकीन है कि मैं यहाँ कुछ कमी नहीं कर रहा हूँ करना चाहते हैं ...

+6

पेजिंग डॉक्टर [@ हावर्ड हिन्नेंट] (http://stackoverflow.com/users/576911/howard-hinnant) – sehe

उत्तर

23

से पहले मैं अपने पिटारे में std::max_align_t था (जो अब <cstddef> में रहती है) यह कोड लिखा गया था। अब मैं इसे इस प्रकार लिखूंगा:

static const std::size_t alignment = alignof(std::max_align_t); 

जो मेरी प्रणाली पर वर्तमान कोड के बराबर है, लेकिन अब और अधिक पोर्टेबल है। यह संरेखण है जो new और malloc वापस लौटने की गारंटी है। और एक बार जब आपके पास यह "अधिकतम गठबंधन" बफर होता है, तो आप इसमें किसी एक प्रकार का सरणी डाल सकते हैं। लेकिन आप अलग-अलग प्रकार के लिए arena का उपयोग नहीं कर सकते हैं (कम से कम अलग-अलग प्रकार के अलग-अलग संरेखण आवश्यकताओं वाले नहीं)। और इसी कारण से, शायद यह पर दूसरे size_t पर टेम्पलेट के लिए सबसे अच्छा होगा, जो alignof(T) के बराबर है। कि जिस तरह से आप एक ही arena से गलती से भिन्न संरेखण आवश्यकताओं के साथ प्रकार के द्वारा प्रयोग किया जा रहा रोका जा सकता है:

arena<N, alignof(T)>& a_; 

ही संरेखण आवश्यकताओं arena से प्रत्येक आवंटन मानते हुए है, और बफर संभालने अधिकतम गठबंधन है, तो हर आवंटन बफर T के लिए उपयुक्त रूप से गठबंधन किया जाएगा।

उदा। मेरे सिस्टम alignof(std::max_align_t) == 16 पर। इस संरेखण के साथ एक बफर के

  • alignof == 1 के साथ प्रकार हो सकते हैं।
  • alignof == 2 के साथ प्रकार।
  • alignof == 4 के साथ प्रकार।
  • alignof == 8 के साथ प्रकार।
  • alignof == 16 के साथ प्रकार।

कुछ वातावरण है कि "सुपर संरेखण" आवश्यकताओं प्रकार का समर्थन कर सकते हैं, एक अतिरिक्त सुरक्षा एहतियात जोड़ने के लिए होगा (short_alloc भीतर कहते हैं):

static_assert(alignof(T) <= alignof(std::max_align_t), ""); 

आप सुपर पागल तुम भी जांच कर सकता है कर रहे हैं कि alignof(T) 2 की शक्ति है, हालांकि सी ++ मानक स्वयं गारंटी देता है कि यह हमेशा सत्य होगा ([basic.align]/p4)।

अद्यतन

मैं इस समस्या को करीब से देख लिया है और विश्वास है कि अगले alignment करने का अनुरोध किया आवंटन आकार (के रूप में ओ पी सुझाव) गोलाई सबसे अच्छा समाधान है है। मैंने अपनी वेबसाइट पर ऐसा करने के लिए "short_alloc" अपडेट किया है।

template <std::size_t N> 
char* 
arena<N>::allocate(std::size_t n) 
{ 
    assert(pointer_in_buffer(ptr_) && "short_alloc has outlived arena"); 
    n = align_up(n); 
    if (buf_ + N - ptr_ >= n) 
    { 
     char* r = ptr_; 
     ptr_ += n; 
     return r; 
    } 
    return static_cast<char*>(::operator new(n)); 
} 
विशेष स्थितियों में, जहां आप जानते हैं है कि आप अधिकतम गठबंधन आवंटन की जरूरत नहीं है (उदाहरण के लिए vector<unsigned char>), एक बस alignment उचित रूप से ठीक कर सकते हैं के लिए

। और एक भी short_alloc::allocate पास alignof(T)arena::allocate

template <std::size_t N> 
char* 
arena<N>::allocate(std::size_t n, std::size_t requested_align) 
{ 
    assert(requested_align <= alignment); 
    assert(pointer_in_buffer(ptr_) && "short_alloc has outlived arena"); 
    n = align_up(n); 
    if (buf_ + N - ptr_ >= n) 
    { 
     char* r = ptr_; 
     ptr_ += n; 
     return r; 
    } 
    return static_cast<char*>(::operator new(n)); 
} 

करने और assert(requested_align <= alignment) यह आपको विश्वास है कि अगर आप alignment नीचे समायोजित, आप इसे बहुत दूर नीचे समायोजित नहीं किया देना होगा हो सकता था।

फिर से अपडेट करें!

मैंने इस उत्कृष्ट प्रश्न के कारण इस आवंटन के description और code को अद्यतन किया है (मैंने इस कोड को वर्षों से उपेक्षित कर दिया है)।

पिछले अद्यतन में उल्लिखित संरेखण जांच अब संकलन-समय पर किया गया है (संकलन-समय त्रुटियां हमेशा रन-टाइम त्रुटियों से भी बेहतर होती हैं, यहां तक ​​कि आवेषण भी)।

arena और short_alloc दोनों अब संरेखण पर टेम्पलेट किए गए हैं ताकि आप आसानी से संरेखण आवश्यकताओं को अनुकूलित कर सकें (और यदि आप बहुत कम अनुमान लगाते हैं तो यह संकलन समय पर पकड़ा जाता है)। यह टेम्पलेट पैरामीटर alignof(std::max_align_t) पर डिफॉल्ट किया गया है।

arena::allocate समारोह अब की तरह दिखता है:

template <std::size_t N, std::size_t alignment> 
template <std::size_t ReqAlign> 
char* 
arena<N, alignment>::allocate(std::size_t n) 
{ 
    static_assert(ReqAlign <= alignment, "alignment is too small for this arena"); 
    assert(pointer_in_buffer(ptr_) && "short_alloc has outlived arena"); 
    auto const aligned_n = align_up(n); 
    if (buf_ + N - ptr_ >= aligned_n) 
    { 
     char* r = ptr_; 
     ptr_ += aligned_n; 
     return r; 
    } 
    return static_cast<char*>(::operator new(n)); 
} 

उर्फ ​​टेम्पलेट्स के लिए धन्यवाद, यह संभाजक पहले से कहीं ज्यादा उपयोग करने के लिए आसान है। उदाहरण के लिए:

// Create a vector<T> template with a small buffer of 200 bytes. 
// Note for vector it is possible to reduce the alignment requirements 
// down to alignof(T) because vector doesn't allocate anything but T's. 
// And if we're wrong about that guess, it is a comple-time error, not 
// a run time error. 
template <class T, std::size_t BufSize = 200> 
using SmallVector = std::vector<T, short_alloc<T, BufSize, alignof(T)>>; 

// Create the stack-based arena from which to allocate 
SmallVector<int>::allocator_type::arena_type a; 
// Create the vector which uses that arena. 
SmallVector<int> v{a}; 

यह जरूरी नहीं कि इस तरह के allocators में अंतिम शब्द है। लेकिन उम्मीद है कि यह एक ठोस नींव है जिस पर आप अपने कस्टम आवंटकों का निर्माण कर सकते हैं।

+0

विस्तृत उत्तर के लिए बहुत बहुत धन्यवाद। मैंने जो समस्या देखी थी वह बिल्कुल वैसा ही था: "उसी क्षेत्र को अलग-अलग संरेखण आवश्यकताओं वाले प्रकारों द्वारा गलती से उपयोग किया जा रहा है"। अब तक मैंने 'max_align_t' के गुणकों के लिए [डी] आवंटन के आकार को गोल करके" निश्चित "किया है। मुझे 'सुझाव' के टेम्पलेट पैरामीटर के रूप में संरेखण जोड़ने के लिए आपका सुझाव पसंद है, लेकिन मुझे यकीन नहीं है कि यह 'short_alloc :: rebind'' के संयोजन में कैसे काम करेगा। यानी ऐसे मामले नहीं होंगे जहां अंतर्निहित 'क्षेत्र' को दो संबंधित आवंटकों द्वारा साझा नहीं किया जा सके? वैसे भी, मैं इसे आज़मा दूंगा। एक बार फिर धन्यवाद। – pragmatib

+0

@pragmatib: आपका फिक्स मेरे से बेहतर है। मैंने अपना जवाब समायोजित कर लिया है और मेरी वेबसाइट अपडेट की है। –

+0

शांत, अद्यतन कोड के लिए धन्यवाद। 'आवंटित' विधि में, क्या आप गठबंधन-अप मान को पकड़ने के लिए अस्थायी चर नहीं पेश करना चाहिए? इसके बिना, मुझे लगता है कि ':: ऑपरेटर न्यू' पर पतन-बैक थोड़ा अधिक स्मृति का अनुरोध कर सकता है। – pragmatib

संबंधित मुद्दे