シンプルで高速なアロケータ(2)

過去ログ


使い方としてはこんな感じになると思います。

// ブロックの数は 2^15=32768 個で、1つのブロックの大きさは 2^4=16 バイト
typedef SimpleAllocator<15, 4> MyAllocator;
// 必要なメモリを確保して
void* pMemory = ::malloc(MyAllocator::NeedMemorySize);
// 初期化
MyAllocator allocator(pMemory);

...

// Hoge クラス用の operator new
void* Hoge::operator new(size_t size)
{
    void* p = NULL;
    // 要求されたサイズが 16 バイト以下の大きさだったら
    if (size <= MyAllocator::BlockSize)
    {
        // 自前のアロケータを使う
        p = allocator.Use();
    }
    // size が 16 バイトより大きかったか、
    // 自前のアロケータがいっぱいで確保できなかった
    if (p == NULL)
    {
        return ::operator new(size);
    }
    return p;
}

// Hoge クラス用の operator delete
void Hoge::operator delete(void* p)
{
    // Unuse() は、p が自分の確保したメモリだった場合は true を返す
    if (allocator.Unuse(p))
    {
        // 無事解放出来た
        return;
    }
    // allocator の確保したメモリではなかったので ::operator delete で解放
    ::operator delete(p);
}

シンプルだからといって汎用性に手を抜くつもりは無いので、テンプレートを使用してブロックの数と大きさを指定できるようにします。
ただし、中途半端な大きさだと困ることが多いので、2 の整数乗だけしか指定できないようにします。


メモリをアロケートするためには Use() を使用します。
アロケートするサイズは常に一定なので、引数で渡す必要はありません。


メモリを解放するためには Unuse() を使用します。
自分がアロケートしたポインタとそうでないポインタを区別するために、自分のアロケータであれば true を、そうでなければ false を返すようにします。
これはポインタの値が自分のメモリ領域の範囲に入っているかどうかで判断できます。


あと、Hoge が複数のスレッドから new される場合は、マルチスレッドに対しても考慮する必要があります。
これは SimpleAllocator のテンプレート引数で、スレッドモデルを選択できるようにします。

// シングルスレッド用のアロケータ
typedef SimpleAllocator<15, 4, SingleThreadModel> SingleAllocator;
// マルチスレッド用のアロケータ
typedef SimpleAllocator<15, 4, MultiThreadModel> MultiAllocator;
// これはシングルスレッド用になる
typedef SimpleAllocator<15, 4> DefaultAllocator;

デフォルトでは SingleThreadModel が使用されるようにします。


スレッドモデルに関しては最後にやるとして、とりあえずこんな感じで作ってみることにします。