Add a bump allocator.

This CL adds a simple allocator which will provide a chunk of memory of
the given size. It allocates out of slabs of memory.

Change-Id: I9acf59fac88cd6bef260b7ebae7d7b77fd939754
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/127302
Reviewed-by: Ben Clayton <bclayton@google.com>
Kokoro: Kokoro <noreply+kokoro@google.com>
Commit-Queue: Dan Sinclair <dsinclair@chromium.org>
This commit is contained in:
dan sinclair 2023-04-18 16:15:44 +00:00 committed by Dawn LUCI CQ
parent dc071b1098
commit fa3c08d112
4 changed files with 180 additions and 0 deletions

View File

@ -222,6 +222,7 @@ libtint_source_set("libtint_base_src") {
"utils/bitcast.h",
"utils/bitset.h",
"utils/block_allocator.h",
"utils/bump_allocator.h",
"utils/compiler_macros.h",
"utils/concat.h",
"utils/crc32.h",
@ -1588,6 +1589,7 @@ if (tint_build_unittests) {
"utils/bitcast_test.cc",
"utils/bitset_test.cc",
"utils/block_allocator_test.cc",
"utils/bump_allocator_test.cc",
"utils/crc32_test.cc",
"utils/defer_test.cc",
"utils/enum_set_test.cc",

View File

@ -517,6 +517,7 @@ list(APPEND TINT_LIB_SRCS
utils/bitcast.h
utils/bitset.h
utils/block_allocator.h
utils/bump_allocator.h
utils/compiler_macros.h
utils/concat.h
utils/crc32.h
@ -998,6 +999,7 @@ if(TINT_BUILD_TESTS)
utils/bitcast_test.cc
utils/bitset_test.cc
utils/block_allocator_test.cc
utils/bump_allocator_test.cc
utils/crc32_test.cc
utils/defer_test.cc
utils/enum_set_test.cc

View File

@ -0,0 +1,127 @@
// Copyright 2023 The Tint Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef SRC_TINT_UTILS_BUMP_ALLOCATOR_H_
#define SRC_TINT_UTILS_BUMP_ALLOCATOR_H_
#include <array>
#include <cstring>
#include <utility>
#include "src/tint/utils/bitcast.h"
#include "src/tint/utils/math.h"
namespace tint::utils {
constexpr size_t kBlockSize = 64 * 1024;
/// A allocator for chunks of memory. The memory is owned by the BumpAllocator. When the
/// BumpAllocator is freed all of the allocated memory is freed.
class BumpAllocator {
/// Block is linked list of memory blocks.
/// Blocks are allocated out of heap memory.
struct Block {
uint8_t data[kBlockSize];
Block* next;
};
public:
/// Constructor
BumpAllocator() = default;
/// Move constructor
/// @param rhs the BumpAllocator to move
BumpAllocator(BumpAllocator&& rhs) { std::swap(data, rhs.data); }
/// Move assignment operator
/// @param rhs the BumpAllocator to move
/// @return this BumpAllocator
BumpAllocator& operator=(BumpAllocator&& rhs) {
if (this != &rhs) {
Reset();
std::swap(data, rhs.data);
}
return *this;
}
/// Destructor
~BumpAllocator() { Reset(); }
/// Allocates @p size_in_bytes from the current block, or from a newly allocated block if the
/// current block is full.
/// @param size_in_bytes the number of bytes to allocate
/// @returns the pointer to the allocated memory or |nullptr| if the memory can not be allocated
char* Allocate(size_t size_in_bytes) {
auto& block = data.block;
if (block.current_offset + size_in_bytes > kBlockSize) {
// Allocate a new block from the heap
auto* prev_block = block.current;
block.current = new Block;
if (!block.current) {
return nullptr; // out of memory
}
block.current->next = nullptr;
block.current_offset = 0;
if (prev_block) {
prev_block->next = block.current;
} else {
block.root = block.current;
}
}
auto* base = &block.current->data[0];
auto* ptr = reinterpret_cast<char*>(base + block.current_offset);
block.current_offset += size_in_bytes;
data.count++;
return ptr;
}
/// Frees all allocations from the allocator.
void Reset() {
auto* block = data.block.root;
while (block != nullptr) {
auto* next = block->next;
delete block;
block = next;
}
data = {};
}
/// @returns the total number of allocations
size_t Count() const { return data.count; }
private:
BumpAllocator(const BumpAllocator&) = delete;
BumpAllocator& operator=(const BumpAllocator&) = delete;
struct {
struct {
/// The root block of the block linked list
Block* root = nullptr;
/// The current (end) block of the blocked linked list.
/// New allocations come from this block
Block* current = nullptr;
/// The byte offset in #current for the next allocation.
/// Initialized with kBlockSize so that the first allocation triggers a block
/// allocation.
size_t current_offset = kBlockSize;
} block;
size_t count = 0;
} data;
};
} // namespace tint::utils
#endif // SRC_TINT_UTILS_BUMP_ALLOCATOR_H_

View File

@ -0,0 +1,49 @@
// Copyright 2023 The Tint Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "src/tint/utils/bump_allocator.h"
#include "gtest/gtest.h"
namespace tint::utils {
namespace {
using BumpAllocatorTest = testing::Test;
TEST_F(BumpAllocatorTest, Count) {
for (size_t n : {0u, 1u, 10u, 16u, 20u, 32u, 50u, 64u, 100u, 256u, 300u, 512u, 500u, 512u}) {
BumpAllocator allocator;
EXPECT_EQ(allocator.Count(), 0u);
for (size_t i = 0; i < n; i++) {
allocator.Allocate(5);
}
EXPECT_EQ(allocator.Count(), n);
}
}
TEST_F(BumpAllocatorTest, MoveConstruct) {
for (size_t n : {0u, 1u, 10u, 16u, 20u, 32u, 50u, 64u, 100u, 256u, 300u, 512u, 500u, 512u}) {
BumpAllocator allocator_a;
for (size_t i = 0; i < n; i++) {
allocator_a.Allocate(5);
}
EXPECT_EQ(allocator_a.Count(), n);
BumpAllocator allocator_b{std::move(allocator_a)};
EXPECT_EQ(allocator_b.Count(), n);
}
}
} // namespace
} // namespace tint::utils