From fa3c08d112bd138cb396df64f205366a98bae6f8 Mon Sep 17 00:00:00 2001 From: dan sinclair Date: Tue, 18 Apr 2023 16:15:44 +0000 Subject: [PATCH] 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 Kokoro: Kokoro Commit-Queue: Dan Sinclair --- src/tint/BUILD.gn | 2 + src/tint/CMakeLists.txt | 2 + src/tint/utils/bump_allocator.h | 127 ++++++++++++++++++++++++++ src/tint/utils/bump_allocator_test.cc | 49 ++++++++++ 4 files changed, 180 insertions(+) create mode 100644 src/tint/utils/bump_allocator.h create mode 100644 src/tint/utils/bump_allocator_test.cc diff --git a/src/tint/BUILD.gn b/src/tint/BUILD.gn index 1e084a2237..5240dd2ebc 100644 --- a/src/tint/BUILD.gn +++ b/src/tint/BUILD.gn @@ -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", diff --git a/src/tint/CMakeLists.txt b/src/tint/CMakeLists.txt index 4dd1c88d7f..6c7acfb426 100644 --- a/src/tint/CMakeLists.txt +++ b/src/tint/CMakeLists.txt @@ -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 diff --git a/src/tint/utils/bump_allocator.h b/src/tint/utils/bump_allocator.h new file mode 100644 index 0000000000..302f924bcb --- /dev/null +++ b/src/tint/utils/bump_allocator.h @@ -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 +#include +#include + +#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(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_ diff --git a/src/tint/utils/bump_allocator_test.cc b/src/tint/utils/bump_allocator_test.cc new file mode 100644 index 0000000000..4f224558fd --- /dev/null +++ b/src/tint/utils/bump_allocator_test.cc @@ -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