Skip to content

Commit

Permalink
pw_allocator: Add hooks to test_harness
Browse files Browse the repository at this point in the history
This CL adds hook functions that cna be overridden before and after each
call to Allocate, Deallocate, and Reallocate. This allows later CLs to
add extra handling to each request, e.g. recording timestamps for
benchmarking.

It also pulls the PRNG into the test harness, as this simplifies its
usage.

Change-Id: I27bdb52e62f5ab13a6be5d6855baa54542d2841f
Reviewed-on: https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/238416
Docs-Not-Needed: Aaron Green <[email protected]>
Commit-Queue: Aaron Green <[email protected]>
Reviewed-by: Taylor Cramer <[email protected]>
Lint: Lint 🤖 <[email protected]>
  • Loading branch information
nopsledder authored and CQ Bot Account committed Sep 30, 2024
1 parent c25923e commit 1d143bb
Show file tree
Hide file tree
Showing 4 changed files with 135 additions and 82 deletions.
5 changes: 2 additions & 3 deletions pw_allocator/examples/custom_allocator_perf_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,14 @@
#include "examples/custom_allocator_test_harness.h"
#include "pw_perf_test/perf_test.h"
#include "pw_perf_test/state.h"
#include "pw_random/xor_shift.h"

namespace examples {

void PerformAllocations(pw::perf_test::State& state, uint64_t seed) {
static CustomAllocatorTestHarness harness;
pw::random::XorShiftStarRng64 prng(seed);
harness.set_prng_seed(seed);
while (state.KeepRunning()) {
harness.GenerateRequest(prng, CustomAllocatorTestHarness::kCapacity);
harness.GenerateRequest(CustomAllocatorTestHarness::kCapacity);
}
harness.Reset();
}
Expand Down
61 changes: 49 additions & 12 deletions pw_allocator/public/pw_allocator/test_harness.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,13 @@
#pragma once

#include <cstddef>
#include <optional>
#include <variant>

#include "pw_allocator/allocator.h"
#include "pw_containers/intrusive_list.h"
#include "pw_containers/vector.h"
#include "pw_random/random.h"
#include "pw_random/xor_shift.h"

namespace pw::allocator::test {

Expand Down Expand Up @@ -81,29 +82,32 @@ class TestHarness {
public:
/// Associates a pointer to memory with the `Layout` used to allocate it.
struct Allocation : public IntrusiveList<Allocation>::Item {
size_t index;
Layout layout;

Allocation(size_t index_, Layout layout_);
explicit Allocation(Layout layout_) : layout(layout_) {}
};

virtual ~TestHarness() = default;

size_t num_allocations() const { return num_allocations_; }
size_t allocated() const { return allocated_; }

void set_prng_seed(uint64_t seed) { prng_ = random::XorShiftStarRng64(seed); }
void set_available(size_t available) { available_ = available; }

/// Generates and handles a sequence of allocation requests.
///
/// This method will use the given PRNG to generate `num_requests` allocation
/// requests and pass each in turn to `HandleRequest`. It will call `Reset`
/// before returning.
void GenerateRequests(random::RandomGenerator& prng,
size_t max_size,
size_t num_requests);
void GenerateRequests(size_t max_size, size_t num_requests);

/// Generate and handle an allocation requests.
///
/// This method will use the given PRNG to generate an allocation request
/// and pass it to `HandleRequest`. Callers *MUST* call `Reset` when no more
/// requests remain to be generated.
void GenerateRequest(random::RandomGenerator& prng, size_t max_size);
void GenerateRequest(size_t max_size);

/// Handles a sequence of allocation requests.
///
Expand All @@ -127,21 +131,42 @@ class TestHarness {
/// If the request is a reallocation request:
/// * If the vector of previous allocations is empty, reallocates a `nullptr`.
/// * Otherwise, removes a pointer from the vector and reallocates it.
void HandleRequest(const Request& request);
///
/// Returns whether the request was handled. This is different from whether
/// the request succeeded, e.g. a DeallocationRequest cannot be handled when
/// there are no current allocations and will return false. By contrast, an
/// AllocationRequest may be handled, but fail due to insufficient memory, and
/// will return true.
bool HandleRequest(const Request& request);

/// Deallocates any pointers stored in the vector of allocated pointers.
void Reset();

private:
virtual Allocator* Init() = 0;

/// Generate requests. `set_prng_seed` must have been called first.
AllocationRequest GenerateAllocationRequest(size_t max_size);
DeallocationRequest GenerateDeallocationRequest();
ReallocationRequest GenerateReallocationRequest(size_t max_size);
size_t GenerateSize(size_t max_size);

/// Derived classes may add callbacks that are invoked before and after each
/// de/re/allocation to record additional data about the allocator.
virtual void BeforeAllocate(const Layout&) {}
virtual void AfterAllocate(const void*) {}
virtual void BeforeReallocate(const Layout&) {}
virtual void AfterReallocate(const void*) {}
virtual void BeforeDeallocate(const void*) {}
virtual void AfterDeallocate() {}

/// Adds a pointer to the vector of allocated pointers.
///
/// The `ptr` must not be null, and the vector of allocated pointers must not
/// be full. To aid in detecting memory corruptions and in debugging, the
/// pointed-at memory will be filled with as much of the following sequence as
/// will fit:
/// * The request number.
/// * The request number.random::RandomGenerator& prng
/// * The request size.
/// * The byte "0x5a", repeating.
void AddAllocation(void* ptr, Layout layout);
Expand All @@ -157,11 +182,23 @@ class TestHarness {
/// A list of allocated pointers.
IntrusiveList<Allocation> allocations_;

/// The number of requests this object has handled.
size_t num_requests_ = 0;

/// The number of outstanding active allocation by this object.
size_t num_allocations_ = 0;

/// The total memory allocated.
size_t allocated_ = 0;

/// An optional amount of memory available. If set, this is used to adjust the
/// likelihood of what requests are generated based on how much of the
/// available memory has been used.
std::optional<size_t> available_;

/// Pseudorandom number generator.
std::optional<random::XorShiftStarRng64> prng_;

/// If an allocation fails, the next generated request is limited to half the
/// previous request's size.
std::optional<size_t> max_size_;
};

} // namespace pw::allocator::test
7 changes: 3 additions & 4 deletions pw_allocator/synchronized_allocator_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@
#include "pw_allocator/test_harness.h"
#include "pw_allocator/testing.h"
#include "pw_containers/vector.h"
#include "pw_random/xor_shift.h"
#include "pw_status/status_with_size.h"
#include "pw_sync/binary_semaphore.h"
#include "pw_sync/interrupt_spin_lock.h"
Expand Down Expand Up @@ -117,8 +116,9 @@ class Background final {
BackgroundThreadCore(pw::Allocator& allocator,
uint64_t seed,
size_t iterations)
: prng_(seed), iterations_(iterations) {
: iterations_(iterations) {
test_harness_.allocator = &allocator;
test_harness_.set_prng_seed(seed);
}

void Stop() { semaphore_.release(); }
Expand All @@ -131,14 +131,13 @@ class Background final {
private:
void Run() override {
for (size_t i = 0; i < iterations_ && !semaphore_.try_acquire(); ++i) {
test_harness_.GenerateRequests(prng_, kMaxSize, kBackgroundRequests);
test_harness_.GenerateRequests(kMaxSize, kBackgroundRequests);
pw::this_thread::yield();
}
semaphore_.release();
}

TestHarness test_harness_;
pw::random::XorShiftStarRng64 prng_;
pw::sync::BinarySemaphore semaphore_;
size_t iterations_;
} background_;
Expand Down
Loading

0 comments on commit 1d143bb

Please sign in to comment.