Skip to content

Commit

Permalink
pw_allocator: Add TLSF allocator
Browse files Browse the repository at this point in the history
The CL adds a two-layer, segregated fit allocator, as described by
http://www.gii.upv.es/tlsf/files/papers/ecrts04_tlsf.pdf

This allocator can satisfy requests quickly, as the cost of the overhead
of its 2D array of buckets.

This performs about the same as the WorstFitAllocator, i.e ~22 us per
request when the allocator is tracking several thousand allocations. It
has much better fragmentation, however, with a fragmentation score of
~3% to WorstFitAllocator's 56%. This leads to more usable memory being
available under the same circumstances, e.g. 37M out of 64M as opposed
to WorstFit's 20.5M.

Change-Id: I12fe987d32d50377418f2108f22a752f25bdfa52
Reviewed-on: https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/234818
Reviewed-by: Wyatt Hepler <[email protected]>
Commit-Queue: Aaron Green <[email protected]>
Lint: Lint 🤖 <[email protected]>
  • Loading branch information
nopsledder authored and CQ Bot Account committed Dec 26, 2024
1 parent 8be090f commit f674d68
Show file tree
Hide file tree
Showing 14 changed files with 598 additions and 2 deletions.
1 change: 1 addition & 0 deletions docs/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,7 @@ _doxygen_input_files = [ # keep-sorted: start
"$dir_pw_allocator/public/pw_allocator/synchronized_allocator.h",
"$dir_pw_allocator/public/pw_allocator/test_harness.h",
"$dir_pw_allocator/public/pw_allocator/testing.h",
"$dir_pw_allocator/public/pw_allocator/tlsf_allocator.h",
"$dir_pw_allocator/public/pw_allocator/tracking_allocator.h",
"$dir_pw_allocator/public/pw_allocator/typed_pool.h",
"$dir_pw_allocator/public/pw_allocator/unique_ptr.h",
Expand Down
24 changes: 24 additions & 0 deletions pw_allocator/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -374,6 +374,20 @@ cc_library(
],
)

cc_library(
name = "tlsf_allocator",
hdrs = ["public/pw_allocator/tlsf_allocator.h"],
includes = ["public"],
deps = [
":block_allocator",
":config",
"//pw_allocator/block:detailed_block",
"//pw_allocator/bucket:fast_sorted",
"//pw_allocator/bucket:sorted",
"//third_party/fuchsia:stdcompat",
],
)

cc_library(
name = "tracking_allocator",
hdrs = [
Expand Down Expand Up @@ -732,6 +746,16 @@ pw_cc_test(
],
)

pw_cc_test(
name = "tlsf_allocator_test",
srcs = ["tlsf_allocator_test.cc"],
deps = [
":block_allocator_testing",
":tlsf_allocator",
"//pw_unit_test",
],
)

pw_cc_test(
name = "tracking_allocator_test",
srcs = [
Expand Down
27 changes: 27 additions & 0 deletions pw_allocator/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -316,6 +316,19 @@ pw_source_set("synchronized_allocator") {
]
}

pw_source_set("tlsf_allocator") {
public_configs = [ ":public_include_path" ]
public = [ "public/pw_allocator/tlsf_allocator.h" ]
public_deps = [
":block_allocator",
":config",
"$dir_pw_third_party/fuchsia:stdcompat",
"block:detailed_block",
"bucket:fast_sorted",
"bucket:sorted",
]
}

pw_source_set("tracking_allocator") {
public_configs = [ ":public_include_path" ]
public = [
Expand Down Expand Up @@ -586,6 +599,14 @@ pw_test("synchronized_allocator_test") {
sources = [ "synchronized_allocator_test.cc" ]
}

pw_test("tlsf_allocator_test") {
deps = [
":block_allocator_testing",
":tlsf_allocator",
]
sources = [ "tlsf_allocator_test.cc" ]
}

pw_test("tracking_allocator_test") {
deps = [
":testing",
Expand Down Expand Up @@ -638,6 +659,7 @@ pw_test_group("tests") {
":null_allocator_test",
":pmr_allocator_test",
":synchronized_allocator_test",
":tlsf_allocator_test",
":tracking_allocator_test",
":typed_pool_test",
":unique_ptr_test",
Expand Down Expand Up @@ -702,6 +724,11 @@ pw_size_diff("concrete_allocators_size_report") {
base = "size_report:null_allocator"
label = "LibCAllocator"
},
{
target = "size_report:tlsf_allocator"
base = "size_report:null_allocator"
label = "TlsfAllocator"
},
{
target = "size_report:worst_fit"
base = "size_report:null_allocator"
Expand Down
25 changes: 25 additions & 0 deletions pw_allocator/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -342,6 +342,20 @@ pw_add_library(pw_allocator.synchronized_allocator INTERFACE
pw_sync.borrow
)

pw_add_library(pw_allocator.tlsf_allocator INTERFACE
HEADERS
public/pw_allocator/tlsf_allocator.h
PUBLIC_INCLUDES
public
PUBLIC_DEPS
pw_allocator.block_allocator
pw_allocator.block.detailed_block
pw_allocator.bucket.sorted
pw_allocator.bucket.fast_sorted
pw_allocator.config
pw_third_party.fuchsia.stdcompat
)

pw_add_library(pw_allocator.tracking_allocator INTERFACE
HEADERS
public/pw_allocator/metrics.h
Expand Down Expand Up @@ -675,6 +689,17 @@ pw_add_test(pw_allocator.synchronized_allocator_test
pw_allocator
)

pw_add_test(pw_allocator.tlsf_allocator_test
SOURCES
tlsf_allocator_test.cc
PRIVATE_DEPS
pw_allocator.block_allocator_testing
pw_allocator.tlsf_allocator
GROUPS
modules
pw_allocator
)

pw_add_test(pw_allocator.tracking_allocator_test
SOURCES
tracking_allocator_test.cc
Expand Down
14 changes: 12 additions & 2 deletions pw_allocator/api.rst
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,9 @@ This module provides several concrete allocator implementations of the

BlockAllocator
==============
Several allocators use :ref:`module-pw_allocator-api-block` types to manage
memory, and derive from this abstract base type.

.. doxygenclass:: pw::allocator::BlockAllocator
:members:

Expand All @@ -113,6 +116,13 @@ BestFitAllocator
.. doxygenclass:: pw::allocator::BestFitAllocator
:members:

.. _module-pw_allocator-api-tlsf_allocator:

TlsfAllocator
-------------
.. doxygenclass:: pw::allocator::TlsfAllocator
:members:

.. _module-pw_allocator-api-worst_fit_allocator:

WorstFitAllocator
Expand Down Expand Up @@ -218,8 +228,8 @@ includes some utility classes.

.. _module-pw_allocator-api-block:

Block interfaces
================
Block
=====
A block is an allocatable region of memory, and is the fundamental type managed
by several of the concrete allocator implementations. Blocks are defined
using several stateless "mix-in" interface types. These provide specific
Expand Down
13 changes: 13 additions & 0 deletions pw_allocator/benchmarks/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,19 @@ cc_binary(
],
)

cc_binary(
name = "tlsf_benchmark",
testonly = True,
srcs = [
"tlsf_benchmark.cc",
],
deps = [
":benchmark",
"//pw_allocator:tlsf_allocator",
"//pw_random",
],
)

cc_binary(
name = "worst_fit_benchmark",
testonly = True,
Expand Down
9 changes: 9 additions & 0 deletions pw_allocator/benchmarks/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,15 @@ pw_executable("last_fit_benchmark") {
]
}

pw_executable("tlsf_benchmark") {
sources = [ "tlsf_benchmark.cc" ]
deps = [
":benchmark",
"$dir_pw_allocator:tlsf_allocator",
"$dir_pw_random",
]
}

pw_executable("worst_fit_benchmark") {
sources = [ "worst_fit_benchmark.cc" ]
deps = [
Expand Down
44 changes: 44 additions & 0 deletions pw_allocator/benchmarks/tlsf_benchmark.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
// Copyright 2024 The Pigweed 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
//
// https://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 <array>
#include <cstddef>
#include <cstdint>

#include "pw_allocator/benchmarks/benchmark.h"
#include "pw_allocator/benchmarks/config.h"
#include "pw_allocator/tlsf_allocator.h"

namespace pw::allocator {

constexpr metric::Token kTlsfBenchmark =
PW_TOKENIZE_STRING("two-layer, segregated-fit benchmark");

std::array<std::byte, benchmarks::kCapacity> buffer;

void DoTlsfBenchmark() {
TlsfAllocator allocator(buffer);
DefaultBlockAllocatorBenchmark benchmark(kTlsfBenchmark, allocator);
benchmark.set_prng_seed(1);
benchmark.set_available(benchmarks::kCapacity);
benchmark.GenerateRequests(benchmarks::kMaxSize, benchmarks::kNumRequests);
benchmark.metrics().Dump();
}

} // namespace pw::allocator

int main() {
pw::allocator::DoTlsfBenchmark();
return 0;
}
Loading

0 comments on commit f674d68

Please sign in to comment.