Skip to content

Commit

Permalink
CMemPoolのBlock構造を言語の型機能を使って実現するように変更
Browse files Browse the repository at this point in the history
  • Loading branch information
beru committed Aug 4, 2019
1 parent 9a2f705 commit 8110e42
Show file tree
Hide file tree
Showing 2 changed files with 38 additions and 60 deletions.
58 changes: 28 additions & 30 deletions sakura_core/mem/CMemPool.h
Original file line number Diff line number Diff line change
Expand Up @@ -45,10 +45,10 @@ class CMemPool final
~CMemPool()
{
// メモリ確保した領域の連結リストを辿って全てのブロック分のメモリ解放
Node* curr = m_currentBlock;
Block* curr = m_currentBlock;
while (curr) {
Node* next = curr->next;
operator delete(reinterpret_cast<void*>(curr));
Block* next = curr->next;
operator delete(curr);
curr = next;
}
}
Expand All @@ -72,36 +72,40 @@ class CMemPool final
}

private:
// 共用体を使う事で要素型と自己参照用のポインタを同じ領域に割り当てる
// 共用体のサイズは各メンバを格納できるサイズになる事を利用する

union Node {
~Node() {}
T element; // 要素型
Node* next; // ブロックのヘッダの場合は、次のブロックに繋がる
// 解放後の未割当領域の場合は次の未割当領域に繋がる
Node* next; // 解放後の未割当領域の場合は次の未割当領域に繋がる
};

// ブロックの大きさをNode2個分以上とする事で、最低限ブロック連結用のポインタとNode1つを記録出来る事を保証
static_assert(BlockSize >= 2 * sizeof(Node), "BlockSize too small.");

union Block {
~Block() {}
struct {
Block* next;
Node nodes[1];
};
uint8_t padding[BlockSize];
};

// 要素のメモリ確保処理、要素の領域のポインタを返す
T* Allocate()
{
// メモリ確保時には未割当領域から使用していく
if (m_unassignedNode) {
T* ret = reinterpret_cast<T*>(m_unassignedNode);
T* ret = &m_unassignedNode->element;
m_unassignedNode = m_unassignedNode->next;
return ret;
}
else {
// 未割当領域が無い場合は、ブロックの中から切り出す
// 現在のブロックにNodeサイズ分の領域が無い場合は新規のブロックを確保
Node* border = reinterpret_cast<Node*>(reinterpret_cast<char*>(m_currentBlock) + BlockSize - sizeof(Node) + 1);
if (m_currentNode >= border) {
// 未割当領域が無い場合は、ブロックの中のNode領域を使用する
if (reinterpret_cast<void*>(m_currentNode + 1) >= reinterpret_cast<void*>(m_currentBlock + 1)) {
// 現在のブロックに新規に割り当てるNode分の領域が残っていない場合は新規にブロックを確保
AllocateBlock();
}
// 要素の領域のポインタを返すと同時にポインタを次に進めて切り出す位置を更新する
return reinterpret_cast<T*>(m_currentNode++);
T* ret = &m_currentNode->element;
++m_currentNode;
return ret;
}
}

Expand All @@ -119,26 +123,20 @@ class CMemPool final
}

// 呼び出しの度にメモリの動的確保を細かく行う事を避ける為に、一括でブロック領域を確保
// ブロックの先頭(head)にはブロックの連結用のポインタが配置され、残る領域(body)には要素が記録される
void AllocateBlock()
{
char* buff = reinterpret_cast<char*>(operator new (BlockSize));
Node* next = m_currentBlock;
// ブロック領域の先頭(head)はNodeのポインタとして扱い、以前に作成したブロックに連結する
m_currentBlock = reinterpret_cast<Node*>(buff);
Block* next = m_currentBlock;
// 以前に作成したブロックに連結する
m_currentBlock = reinterpret_cast<Block*>(operator new(sizeof(Block)));
m_currentBlock->next = next;

// ブロック領域の残る部分は要素の領域とするが、アライメントを取る
void* body = buff + sizeof(Node*);
size_t space = BlockSize - sizeof(Node*);
body = std::align(alignof(Node), sizeof(Node), body, space);
assert(body);
m_currentNode = reinterpret_cast<Node*>(body);
// 新規に作成したブロックの先頭のNodeから使用する
m_currentNode = &m_currentBlock->nodes[0];
}

Block* m_currentBlock = nullptr; // 現在のブロック
Node* m_unassignedNode = nullptr; // 未割当領域の先頭
Node* m_currentBlock = nullptr; // 現在のブロック
Node* m_currentNode = nullptr; // 要素確保処理時に現在のブロックの中から切り出すNodeを指すポインタ、メモリ確保時に未割当領域が無い場合はここを使う
Node* m_currentNode = nullptr; // 現在のブロックの中のNodeを指すポインタ、メモリ確保時に未割当領域が無い場合はここを使う
};

#endif /* SAKURA_CMEMPOOL_H_ */
40 changes: 10 additions & 30 deletions tests/unittests/test-cmempool.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,16 @@ class CMemPoolTest : public ::testing::Test{};

// BlockSize テンプレート引数のバリエーション用意
using test_types = ::testing::Types<
std::integral_constant<std::size_t, 511>,
std::integral_constant<std::size_t, 1>,
std::integral_constant<std::size_t, 2>,
std::integral_constant<std::size_t, 3>,
std::integral_constant<std::size_t, 4>,
std::integral_constant<std::size_t, 8>,
std::integral_constant<std::size_t, 16>,
std::integral_constant<std::size_t, 32>,
std::integral_constant<std::size_t, 64>,
std::integral_constant<std::size_t, 128>,
std::integral_constant<std::size_t, 256>,
std::integral_constant<std::size_t, 512>,
std::integral_constant<std::size_t, 1024>,
std::integral_constant<std::size_t, 2048>,
Expand Down Expand Up @@ -160,32 +169,3 @@ TYPED_TEST(CMemPoolTest, parameterized_constructor)
}
}

// ブロックサイズは要素が2つ以上入る大きさにする確認
TEST(CMemPool, BlockSize)
{
// ブロックサイズが要素が2つ以上入る大きさに指定した場合にコンパイルエラーが起きない事の確認
CMemPool<std::array<uint8_t, 1024>, 2048> pool0;
CMemPool<std::array<uint8_t, 1025>, 4096> pool1;
CMemPool<std::array<uint8_t, 2048>, 4096> pool2;
CMemPool<std::array<uint8_t, 2049>, 8192> pool3;
CMemPool<std::array<uint8_t, 4096>, 8192> pool4;

// ブロックサイズを最小の大きさにした際に要素の構築と破棄が正常に動作するかを確認
{
CMemPool<char, sizeof(void*)*2> pool;
char* p;
p = pool.Construct();
pool.Destruct(p);
p = pool.Construct();
pool.Destruct(p);
}
{
CMemPool<double, 16> pool;
double* p;
p = pool.Construct();
pool.Destruct(p);
p = pool.Construct();
pool.Destruct(p);
}
}

0 comments on commit 8110e42

Please sign in to comment.