2015-11-15 10 views
20

私は最近、Howard Hinnantのshort_allocに出くわしました。これは私が見たカスタムアロケータの一番良い例です。Hinnantの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)); 
} 

:実際には、私は、関連するコードフラグメント下記参照、最初の割り当てが(バッファ自体が強制アライメントを持っているとして)適切に整列されることが保証されていることを恐れます(メモリの浪費を犠牲にして)sizeallocate/deallocateに丸めるのが最も簡単な方法は、alignmentの倍数にすることです。私は私のツールボックスにstd::max_align_tを持っていた前

しかし、何かを変更する前に、私は私がここで何かが足りないわけではないことを確認したいと思いますが...

+6

ページングドクター[@HowardHinnant](http://stackoverflow.com/users/576911/howard-hinnant) – sehe

答えて

23

このコードは書かれていた(今<cstddef>に住んでいました)。私は今、次のように書いています:

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

私のシステムでは、現在のコードと正確に同じですが、今はより移植性があります。これはnewmallocの返品が保証されています。そして、この「最大限整列された」バッファーがあれば、その中に任意の型の配列を置くことができます。しかし、異なるタイプ(少なくとも異なるアライメント要件を持つタイプではない)に対して同じarenaを使用することはできません。そのため、おそらくarenaのテンプレートをsize_talignof(T)と同じにするのが最善でしょう。そのように誤って異なる配向要件タイプによって使用されるのと同じarenaを防止することができる:arenaから各割当を仮定

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

は、同じアライメント要件があり、その後、バッファを最大限に整列されているからすべての割り当てを仮定しますバッファは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の累乗ですが、C++標準ではこれが常に真実であることが保証されています([basic.align]/p4)。私はこの問題について詳しく見て撮影して、次のalignmentに要求された割り当てサイズアップを(OPが示唆したように)丸めが最善の解決策であると信じている

を更新。私は"short_alloc"を私のウェブサイトで更新しました。あなたが最大限に整列の割り当て(例えばvector<unsigned char>)を必要としないを知っ特殊な状況では

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

、一つは単に適切alignmentを微調整することができます。そして、もう一つは、また、これはあなたが下向きalignmentを調整した場合、あなたはあまりにも遠く下向きに調整していないという確信を与えるだろうalignof(T)

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

arena::allocateにとassert(requested_align <= alignment)short_alloc::allocateパスを持つことができます。

もう一度アップデートしてください!

私はこの優れた質問のためにこのアロケータのdescriptioncodeをかなり更新しました(私はこのコードを長年無視していました)。

以前のアップデートで言及されたアライメントチェックはコンパイル時に行われました(コンパイル時エラーは常にランタイムエラーよりも優れており、アサートさえも優れています)。

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

これは必ずしもそのようなアロケータの最後の単語ではありません。しかし、うまくいけば、これはあなたのカスタムアロケータを構築するための強固な基盤です。

+0

詳細な回答ありがとうございました。私が見ていた問題は、まったく同じことでした。「同じアリーナが、異なるアラインメント要件を持つタイプによって誤って使用されていました。これまでは、[de]割り振りのサイズを 'max_align_t'の倍数に切り上げることで"固定 "しました。私は 'arena'のテンプレートパラメータとしてアラインメントを追加するようあなたの提案を気に入っていますが、これが' short_alloc :: rebind'との組み合わせでどのように動作するかはわかりません。すなわち、基礎となる「アリーナ」が2つの関連するアロケータによってもはや共有され得ない場合はないだろうか?とにかく、私はそれを試してみましょう。再度、感謝します。 – pragmatib

+0

@pragmatib:あなたの修正は私のものより優れています。私は私の答えを調整し、私のウェブサイトを更新しました。 –

+0

クール、更新されたコードをありがとう。 'allocate'メソッドでは、整列された値を保持する一時変数を導入すべきではありませんか?それがなければ、 ':: operator new'へのフォールバックがあまりにも多くのメモリを要求するかもしれません。 – pragmatib

関連する問題