diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 18:45:59 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 18:45:59 +0000 |
commit | 19fcec84d8d7d21e796c7624e521b60d28ee21ed (patch) | |
tree | 42d26aa27d1e3f7c0b8bd3fd14e7d7082f5008dc /src/test/objectstore/fastbmap_allocator_test.cc | |
parent | Initial commit. (diff) | |
download | ceph-upstream/16.2.11+ds.tar.xz ceph-upstream/16.2.11+ds.zip |
Adding upstream version 16.2.11+ds.upstream/16.2.11+dsupstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/test/objectstore/fastbmap_allocator_test.cc')
-rw-r--r-- | src/test/objectstore/fastbmap_allocator_test.cc | 1066 |
1 files changed, 1066 insertions, 0 deletions
diff --git a/src/test/objectstore/fastbmap_allocator_test.cc b/src/test/objectstore/fastbmap_allocator_test.cc new file mode 100644 index 000000000..c59531985 --- /dev/null +++ b/src/test/objectstore/fastbmap_allocator_test.cc @@ -0,0 +1,1066 @@ +// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- +// vim: ts=8 sw=2 smarttab + +#include <iostream> +#include <gtest/gtest.h> + +#include "os/bluestore/fastbmap_allocator_impl.h" + +class TestAllocatorLevel01 : public AllocatorLevel01Loose +{ +public: + void init(uint64_t capacity, uint64_t alloc_unit) + { + _init(capacity, alloc_unit); + } + interval_t allocate_l1_cont(uint64_t length, uint64_t min_length, + uint64_t pos_start, uint64_t pos_end) + { + return _allocate_l1_contiguous(length, min_length, 0, pos_start, pos_end); + } + void free_l1(const interval_t& r) + { + _free_l1(r.offset, r.length); + } +}; + +class TestAllocatorLevel02 : public AllocatorLevel02<AllocatorLevel01Loose> +{ +public: + void init(uint64_t capacity, uint64_t alloc_unit) + { + _init(capacity, alloc_unit); + } + void allocate_l2(uint64_t length, uint64_t min_length, + uint64_t* allocated0, + interval_vector_t* res) + { + uint64_t allocated = 0; + uint64_t hint = 0; // trigger internal l2 hint support + _allocate_l2(length, min_length, 0, hint, &allocated, res); + *allocated0 += allocated; + } + void free_l2(const interval_vector_t& r) + { + _free_l2(r); + } + void mark_free(uint64_t o, uint64_t len) + { + _mark_free(o, len); + } + void mark_allocated(uint64_t o, uint64_t len) + { + _mark_allocated(o, len); + } +}; + +const uint64_t _1m = 1024 * 1024; +const uint64_t _2m = 2 * 1024 * 1024; + +TEST(TestAllocatorLevel01, test_l1) +{ + TestAllocatorLevel01 al1; + uint64_t num_l1_entries = 3 * 256; + uint64_t capacity = num_l1_entries * 512 * 4096; + al1.init(capacity, 0x1000); + ASSERT_EQ(capacity, al1.debug_get_free()); + + auto i1 = al1.allocate_l1_cont(0x1000, 0x1000, 0, num_l1_entries); + ASSERT_EQ(i1.offset, 0u); + ASSERT_EQ(i1.length, 0x1000u); + ASSERT_EQ(capacity - 0x1000, al1.debug_get_free()); + + auto i2 = al1.allocate_l1_cont(0x1000, 0x1000, 0, num_l1_entries); + ASSERT_EQ(i2.offset, 0x1000u); + ASSERT_EQ(i2.length, 0x1000u); + al1.free_l1(i2); + al1.free_l1(i1); + i1 = al1.allocate_l1_cont(0x1000, 0x1000, 0, num_l1_entries); + ASSERT_EQ(i1.offset, 0u); + ASSERT_EQ(i1.length, 0x1000u); + i2 = al1.allocate_l1_cont(0x1000, 0x1000, 0, num_l1_entries); + ASSERT_EQ(i2.offset, 0x1000u); + ASSERT_EQ(i2.length, 0x1000u); + al1.free_l1(i1); + al1.free_l1(i2); + + i1 = al1.allocate_l1_cont(0x2000, 0x1000, 0, num_l1_entries); + ASSERT_EQ(i1.offset, 0u); + ASSERT_EQ(i1.length, 0x2000u); + + i2 = al1.allocate_l1_cont(0x3000, 0x1000, 0, num_l1_entries); + ASSERT_EQ(i2.offset, 0x2000u); + ASSERT_EQ(i2.length, 0x3000u); + + al1.free_l1(i1); + al1.free_l1(i2); + + i1 = al1.allocate_l1_cont(0x2000, 0x1000, 0, num_l1_entries); + ASSERT_EQ(i1.offset, 0u); + ASSERT_EQ(i1.length, 0x2000u); + + i2 = al1.allocate_l1_cont(2 * 1024 * 1024, 0x1000, 0, num_l1_entries); + ASSERT_EQ(i2.offset, 2u * 1024u * 1024u); + ASSERT_EQ(i2.length, 2u * 1024u * 1024u); + + al1.free_l1(i1); + i1 = al1.allocate_l1_cont(1024 * 1024, 0x1000, 0, num_l1_entries); + ASSERT_EQ(i1.offset, 0u); + ASSERT_EQ(i1.length, 1024u * 1024u); + + auto i3 = al1.allocate_l1_cont(1024 * 1024 + 0x1000, 0x1000, 0, num_l1_entries); + ASSERT_EQ(i3.offset, 2u * 2u * 1024u * 1024u); + ASSERT_EQ(i3.length, 1024u * 1024u + 0x1000u); + + // here we have the following layout: + // Alloc: 0~1M, 2M~2M, 4M~1M+4K + // Free: 1M~1M, 4M+4K ~ 2M-4K, 6M ~... + // + auto i4 = al1.allocate_l1_cont(1024 * 1024, 0x1000, 0, num_l1_entries); + ASSERT_EQ(1 * 1024 * 1024u, i4.offset); + ASSERT_EQ(1024 * 1024u, i4.length); + al1.free_l1(i4); + + i4 = al1.allocate_l1_cont(1024 * 1024 - 0x1000, 0x1000, 0, num_l1_entries); + ASSERT_EQ(i4.offset, 5u * 1024u * 1024u + 0x1000u); + ASSERT_EQ(i4.length, 1024u * 1024u - 0x1000u); + al1.free_l1(i4); + + i4 = al1.allocate_l1_cont(1024 * 1024 + 0x1000, 0x1000, 0, num_l1_entries); + ASSERT_EQ(i4.offset, 6u * 1024u * 1024u); + //ASSERT_EQ(i4.offset, 5 * 1024 * 1024 + 0x1000); + ASSERT_EQ(i4.length, 1024u * 1024u + 0x1000u); + + al1.free_l1(i1); + al1.free_l1(i2); + al1.free_l1(i3); + al1.free_l1(i4); + + i1 = al1.allocate_l1_cont(1024 * 1024, 0x1000, 0, num_l1_entries); + ASSERT_EQ(i1.offset, 0u); + ASSERT_EQ(i1.length, 1024u * 1024u); + + i2 = al1.allocate_l1_cont(1024 * 1024, 0x1000, 0, num_l1_entries); + ASSERT_EQ(i2.offset, 1u * 1024u * 1024u); + ASSERT_EQ(i2.length, 1024u * 1024u); + + i3 = al1.allocate_l1_cont(512 * 1024, 0x1000, 0, num_l1_entries); + ASSERT_EQ(i3.offset, 2u * 1024u * 1024u); + ASSERT_EQ(i3.length, 512u * 1024u); + + i4 = al1.allocate_l1_cont(1536 * 1024, 0x1000, 0, num_l1_entries); + ASSERT_EQ(i4.offset, (2u * 1024u + 512u) * 1024u); + ASSERT_EQ(i4.length, 1536u * 1024u); + // making a hole 1.5 Mb length + al1.free_l1(i2); + al1.free_l1(i3); + // and trying to fill it + i2 = al1.allocate_l1_cont(1536 * 1024, 0x1000, 0, num_l1_entries); + ASSERT_EQ(i2.offset, 1024u * 1024u); + ASSERT_EQ(i2.length, 1536u * 1024u); + + al1.free_l1(i2); + // and trying to fill it partially + i2 = al1.allocate_l1_cont(1528 * 1024, 0x1000, 0, num_l1_entries); + ASSERT_EQ(i2.offset, 1024u * 1024u); + ASSERT_EQ(i2.length, 1528u * 1024u); + + i3 = al1.allocate_l1_cont(8 * 1024, 0x1000, 0, num_l1_entries); + ASSERT_EQ(i3.offset, 2552u * 1024u); + ASSERT_EQ(i3.length, 8u * 1024u); + + al1.free_l1(i2); + // here we have the following layout: + // Alloc: 0~1M, 2552K~8K, num_l1_entries0K~1.5M + // Free: 1M~1528K, 4M ~... + // + i2 = al1.allocate_l1_cont(1536 * 1024, 0x1000, 0, num_l1_entries); + ASSERT_EQ(i2.offset, 4u * 1024u * 1024u); + ASSERT_EQ(i2.length, 1536u * 1024u); + + al1.free_l1(i1); + al1.free_l1(i2); + al1.free_l1(i3); + al1.free_l1(i4); + ASSERT_EQ(capacity, al1.debug_get_free()); + + for (uint64_t i = 0; i < capacity; i += _2m) { + i1 = al1.allocate_l1_cont(_2m, _2m, 0, num_l1_entries); + ASSERT_EQ(i1.offset, i); + ASSERT_EQ(i1.length, _2m); + } + ASSERT_EQ(0u, al1.debug_get_free()); + i2 = al1.allocate_l1_cont(_2m, _2m, 0, num_l1_entries); + ASSERT_EQ(i2.length, 0u); + ASSERT_EQ(0u, al1.debug_get_free()); + + al1.free_l1(i1); + i2 = al1.allocate_l1_cont(_2m, _2m, 0, num_l1_entries); + ASSERT_EQ(i2, i1); + al1.free_l1(i2); + i2 = al1.allocate_l1_cont(_1m, _1m, 0, num_l1_entries); + ASSERT_EQ(i2.offset, i1.offset); + ASSERT_EQ(i2.length, _1m); + + i3 = al1.allocate_l1_cont(_2m, _2m, 0, num_l1_entries); + ASSERT_EQ(i3.length, 0u); + + i3 = al1.allocate_l1_cont(_2m, _1m, 0, num_l1_entries); + ASSERT_EQ(i3.length, _1m); + + i4 = al1.allocate_l1_cont(_2m, _1m, 0, num_l1_entries); + ASSERT_EQ(i4.length, 0u); + + al1.free_l1(i2); + i2 = al1.allocate_l1_cont(_2m, _2m, 0, num_l1_entries); + ASSERT_EQ(i2.length, 0u); + + i2 = al1.allocate_l1_cont(_2m, 0x1000, 0, num_l1_entries); + ASSERT_EQ(i2.length, _1m); + + al1.free_l1(i2); + al1.free_l1(i3); + ASSERT_EQ(_2m, al1.debug_get_free()); + + i1 = al1.allocate_l1_cont(_2m - 3 * 0x1000, 0x1000, 0, num_l1_entries); + i2 = al1.allocate_l1_cont(0x1000, 0x1000, 0, num_l1_entries); + i3 = al1.allocate_l1_cont(0x1000, 0x1000, 0, num_l1_entries); + i4 = al1.allocate_l1_cont(0x1000, 0x1000, 0, num_l1_entries); + ASSERT_EQ(0u, al1.debug_get_free()); + + al1.free_l1(i2); + al1.free_l1(i4); + + i2 = al1.allocate_l1_cont(0x4000, 0x2000, 0, num_l1_entries); + ASSERT_EQ(i2.length, 0u); + i2 = al1.allocate_l1_cont(0x4000, 0x1000, 0, num_l1_entries); + ASSERT_EQ(i2.length, 0x1000u); + + al1.free_l1(i3); + i3 = al1.allocate_l1_cont(0x6000, 0x3000, 0, num_l1_entries); + ASSERT_EQ(i3.length, 0u); + i3 = al1.allocate_l1_cont(0x6000, 0x1000, 0, num_l1_entries); + ASSERT_EQ(i3.length, 0x2000u); + ASSERT_EQ(0u, al1.debug_get_free()); + + std::cout << "Done L1" << std::endl; +} + +TEST(TestAllocatorLevel01, test_l2) +{ + TestAllocatorLevel02 al2; + uint64_t num_l2_entries = 64;// *512; + uint64_t capacity = num_l2_entries * 256 * 512 * 4096; + al2.init(capacity, 0x1000); + std::cout << "Init L2" << std::endl; + + uint64_t allocated1 = 0; + interval_vector_t a1; + al2.allocate_l2(0x2000, 0x2000, &allocated1, &a1); + ASSERT_EQ(allocated1, 0x2000u); + ASSERT_EQ(a1[0].offset, 0u); + ASSERT_EQ(a1[0].length, 0x2000u); + + // limit query range in debug_get_free for the sake of performance + ASSERT_EQ(0x2000u, al2.debug_get_allocated(0, 1)); + ASSERT_EQ(0u, al2.debug_get_allocated(1, 2)); + + uint64_t allocated2 = 0; + interval_vector_t a2; + al2.allocate_l2(0x2000, 0x2000, &allocated2, &a2); + ASSERT_EQ(allocated2, 0x2000u); + ASSERT_EQ(a2[0].offset, 0x2000u); + ASSERT_EQ(a2[0].length, 0x2000u); + // limit query range in debug_get_free for the sake of performance + ASSERT_EQ(0x4000u, al2.debug_get_allocated(0, 1)); + ASSERT_EQ(0u, al2.debug_get_allocated(1, 2)); + + al2.free_l2(a1); + + allocated2 = 0; + a2.clear(); + al2.allocate_l2(0x1000, 0x1000, &allocated2, &a2); + ASSERT_EQ(allocated2, 0x1000u); + ASSERT_EQ(a2[0].offset, 0x0000u); + ASSERT_EQ(a2[0].length, 0x1000u); + // limit query range in debug_get_free for the sake of performance + ASSERT_EQ(0x3000u, al2.debug_get_allocated(0, 1)); + ASSERT_EQ(0u, al2.debug_get_allocated(1, 2)); + + uint64_t allocated3 = 0; + interval_vector_t a3; + al2.allocate_l2(0x2000, 0x1000, &allocated3, &a3); + ASSERT_EQ(allocated3, 0x2000u); + ASSERT_EQ(a3.size(), 2u); + ASSERT_EQ(a3[0].offset, 0x1000u); + ASSERT_EQ(a3[0].length, 0x1000u); + ASSERT_EQ(a3[1].offset, 0x4000u); + ASSERT_EQ(a3[1].length, 0x1000u); + // limit query range in debug_get_free for the sake of performance + ASSERT_EQ(0x5000u, al2.debug_get_allocated(0, 1)); + ASSERT_EQ(0u, al2.debug_get_allocated(1, 2)); + { + interval_vector_t r; + r.emplace_back(0x0, 0x5000); + al2.free_l2(r); + } + + a3.clear(); + allocated3 = 0; + al2.allocate_l2(_1m, _1m, &allocated3, &a3); + ASSERT_EQ(a3.size(), 1u); + ASSERT_EQ(a3[0].offset, 0u); + ASSERT_EQ(a3[0].length, _1m); + + al2.free_l2(a3); + + a3.clear(); + allocated3 = 0; + al2.allocate_l2(4 * _1m, _1m, &allocated3, &a3); + ASSERT_EQ(a3.size(), 1u); + ASSERT_EQ(a3[0].offset, 0u); + ASSERT_EQ(a3[0].length, 4 * _1m); + + al2.free_l2(a3); + +#ifndef _DEBUG + for (uint64_t i = 0; i < capacity; i += 0x1000) { + uint64_t allocated4 = 0; + interval_vector_t a4; + al2.allocate_l2(0x1000, 0x1000, &allocated4, &a4); + ASSERT_EQ(a4.size(), 1u); + ASSERT_EQ(a4[0].offset, i); + ASSERT_EQ(a4[0].length, 0x1000u); + if (0 == (i % (1 * 1024 * _1m))) { + std::cout << "alloc1 " << i / 1024 / 1024 << " mb of " + << capacity / 1024 / 1024 << std::endl; + } + } +#else + for (uint64_t i = 0; i < capacity; i += _2m) { + uint64_t allocated4 = 0; + interval_vector_t a4; + al2.allocate_l2(_2m, _2m, &allocated4, &a4); + ASSERT_EQ(a4.size(), 1); + ASSERT_EQ(a4[0].offset, i); + ASSERT_EQ(a4[0].length, _2m); + if (0 == (i % (1 * 1024 * _1m))) { + std::cout << "alloc1 " << i / 1024 / 1024 << " mb of " + << capacity / 1024 / 1024 << std::endl; + } + } +#endif + + ASSERT_EQ(0u, al2.debug_get_free()); + for (uint64_t i = 0; i < capacity; i += _1m) { + interval_vector_t r; + r.emplace_back(i, _1m); + al2.free_l2(r); + if (0 == (i % (1 * 1024 * _1m))) { + std::cout << "free1 " << i / 1024 / 1024 << " mb of " + << capacity / 1024 / 1024 << std::endl; + } + } + ASSERT_EQ(capacity, al2.debug_get_free()); + + for (uint64_t i = 0; i < capacity; i += _1m) { + uint64_t allocated4 = 0; + interval_vector_t a4; + al2.allocate_l2(_1m, _1m, &allocated4, &a4); + ASSERT_EQ(a4.size(), 1u); + ASSERT_EQ(allocated4, _1m); + ASSERT_EQ(a4[0].offset, i); + ASSERT_EQ(a4[0].length, _1m); + if (0 == (i % (1 * 1024 * _1m))) { + std::cout << "alloc2 " << i / 1024 / 1024 << " mb of " + << capacity / 1024 / 1024 << std::endl; + } + } + ASSERT_EQ(0u, al2.debug_get_free()); + uint64_t allocated4 = 0; + interval_vector_t a4; + al2.allocate_l2(_1m, _1m, &allocated4, &a4); + ASSERT_EQ(a4.size(), 0u); + al2.allocate_l2(0x1000, 0x1000, &allocated4, &a4); + ASSERT_EQ(a4.size(), 0u); + + for (uint64_t i = 0; i < capacity; i += 0x2000) { + interval_vector_t r; + r.emplace_back(i, 0x1000); + al2.free_l2(r); + if (0 == (i % (1 * 1024 * _1m))) { + std::cout << "free2 " << i / 1024 / 1024 << " mb of " + << capacity / 1024 / 1024 << std::endl; + } + } + ASSERT_EQ(capacity / 2, al2.debug_get_free()); + + // unable to allocate due to fragmentation + al2.allocate_l2(_1m, _1m, &allocated4, &a4); + ASSERT_EQ(a4.size(), 0u); + + for (uint64_t i = 0; i < capacity; i += 2 * _1m) { + a4.clear(); + allocated4 = 0; + al2.allocate_l2(_1m, 0x1000, &allocated4, &a4); + ASSERT_EQ(a4.size(), _1m / 0x1000); + ASSERT_EQ(allocated4, _1m); + ASSERT_EQ(a4[0].offset, i); + ASSERT_EQ(a4[0].length, 0x1000u); + if (0 == (i % (1 * 1024 * _1m))) { + std::cout << "alloc3 " << i / 1024 / 1024 << " mb of " + << capacity / 1024 / 1024 << std::endl; + } + } + ASSERT_EQ(0u, al2.debug_get_free()); + + std::cout << "Done L2" << std::endl; +} + +TEST(TestAllocatorLevel01, test_l2_huge) +{ + TestAllocatorLevel02 al2; + uint64_t num_l2_entries = 4 * 512; + uint64_t capacity = num_l2_entries * 256 * 512 * 4096; // 1 TB + al2.init(capacity, 0x1000); + std::cout << "Init L2 Huge" << std::endl; + + for (uint64_t i = 0; i < capacity; i += _1m) { + uint64_t allocated4 = 0; + interval_vector_t a4; + al2.allocate_l2(0x1000, 0x1000, &allocated4, &a4); + ASSERT_EQ(a4.size(), 1u); + ASSERT_EQ(allocated4, 0x1000u); + ASSERT_EQ(a4[0].offset, i); + ASSERT_EQ(a4[0].length, 0x1000u); + + allocated4 = 0; + a4.clear(); + al2.allocate_l2(_1m - 0x1000, 0x1000, &allocated4, &a4); + ASSERT_EQ(a4.size(), 1u); + ASSERT_EQ(allocated4, _1m - 0x1000); + ASSERT_EQ(a4[0].offset, i + 0x1000); + ASSERT_EQ(a4[0].length, _1m - 0x1000); + if (0 == (i % (1 * 1024 * _1m))) { + std::cout << "allocH " << i / 1024 / 1024 << " mb of " + << capacity / 1024 / 1024 << std::endl; + } + } + for (uint64_t i = 0; i < capacity; i += _1m) { + interval_vector_t a4; + a4.emplace_back(i, 0x1000); + al2.free_l2(a4); + if (0 == (i % (1 * 1024 * _1m))) { + std::cout << "freeH1 " << i / 1024 / 1024 << " mb of " + << capacity / 1024 / 1024 << std::endl; + } + } + { + std::cout << "Try" << std::endl; + time_t t = time(NULL); + for (int i = 0; i < 10; ++i) { + uint64_t allocated = 0; + interval_vector_t a; + al2.allocate_l2(0x2000, 0x2000, &allocated, &a); + ASSERT_EQ(a.size(), 0u); + } + std::cout << "End try in " << time(NULL) - t << " seconds" << std::endl; + } + { + std::cout << "Try" << std::endl; + time_t t = time(NULL); + for (int i = 0; i < 10; ++i) { + uint64_t allocated = 0; + interval_vector_t a; + al2.allocate_l2(_2m, _2m, &allocated, &a); + ASSERT_EQ(a.size(), 0u); + } + std::cout << "End try in " << time(NULL) - t << " seconds" << std::endl; + } + + ASSERT_EQ((capacity / _1m) * 0x1000, al2.debug_get_free()); + + std::cout << "Done L2 Huge" << std::endl; +} + +TEST(TestAllocatorLevel01, test_l2_unaligned) +{ + { + TestAllocatorLevel02 al2; + uint64_t num_l2_entries = 3; + uint64_t capacity = num_l2_entries * 256 * 512 * 4096; // 3x512 MB + al2.init(capacity, 0x1000); + std::cout << "Init L2 Unaligned" << std::endl; + + for (uint64_t i = 0; i < capacity; i += _1m / 2) { + uint64_t allocated4 = 0; + interval_vector_t a4; + al2.allocate_l2(_1m / 2, _1m / 2, &allocated4, &a4); + ASSERT_EQ(a4.size(), 1u); + ASSERT_EQ(allocated4, _1m / 2); + ASSERT_EQ(a4[0].offset, i); + ASSERT_EQ(a4[0].length, _1m / 2); + if (0 == (i % (1 * 1024 * _1m))) { + std::cout << "allocU " << i / 1024 / 1024 << " mb of " + << capacity / 1024 / 1024 << std::endl; + } + } + ASSERT_EQ(0u, al2.debug_get_free()); + { + // no space to allocate + uint64_t allocated4 = 0; + interval_vector_t a4; + al2.allocate_l2(0x1000, 0x1000, &allocated4, &a4); + ASSERT_EQ(a4.size(), 0u); + } + } + { + TestAllocatorLevel02 al2; + uint64_t capacity = 500 * 512 * 4096; // 500x2 MB + al2.init(capacity, 0x1000); + std::cout << ("Init L2 Unaligned2\n"); + for (uint64_t i = 0; i < capacity; i += _1m / 2) { + uint64_t allocated4 = 0; + interval_vector_t a4; + al2.allocate_l2(_1m / 2, _1m / 2, &allocated4, &a4); + ASSERT_EQ(a4.size(), 1u); + ASSERT_EQ(allocated4, _1m / 2); + ASSERT_EQ(a4[0].offset, i); + ASSERT_EQ(a4[0].length, _1m / 2); + if (0 == (i % (1 * 1024 * _1m))) { + std::cout << "allocU2 " << i / 1024 / 1024 << " mb of " + << capacity / 1024 / 1024 << std::endl; + } + } + ASSERT_EQ(0u, al2.debug_get_free()); + { + // no space to allocate + uint64_t allocated4 = 0; + interval_vector_t a4; + al2.allocate_l2(0x1000, 0x1000, &allocated4, &a4); + ASSERT_EQ(a4.size(), 0u); + } + } + + { + TestAllocatorLevel02 al2; + uint64_t capacity = 100 * 512 * 4096 + 127 * 4096; + al2.init(capacity, 0x1000); + std::cout << "Init L2 Unaligned2" << std::endl; + for (uint64_t i = 0; i < capacity; i += 0x1000) { + uint64_t allocated4 = 0; + interval_vector_t a4; + al2.allocate_l2(0x1000, 0x1000, &allocated4, &a4); + ASSERT_EQ(a4.size(), 1u); + ASSERT_EQ(allocated4, 0x1000u); + ASSERT_EQ(a4[0].offset, i); + ASSERT_EQ(a4[0].length, 0x1000u); + } + ASSERT_EQ(0u, al2.debug_get_free()); + { + // no space to allocate + uint64_t allocated4 = 0; + interval_vector_t a4; + al2.allocate_l2(0x1000, 0x1000, &allocated4, &a4); + ASSERT_EQ(a4.size(), 0u); + } + } + { + TestAllocatorLevel02 al2; + uint64_t capacity = 3 * 4096; + al2.init(capacity, 0x1000); + std::cout << "Init L2 Unaligned2" << std::endl; + for (uint64_t i = 0; i < capacity; i += 0x1000) { + uint64_t allocated4 = 0; + interval_vector_t a4; + al2.allocate_l2(0x1000, 0x1000, &allocated4, &a4); + ASSERT_EQ(a4.size(), 1u); + ASSERT_EQ(allocated4, 0x1000u); + ASSERT_EQ(a4[0].offset, i); + ASSERT_EQ(a4[0].length, 0x1000u); + } + ASSERT_EQ(0u, al2.debug_get_free()); + { + // no space to allocate + uint64_t allocated4 = 0; + interval_vector_t a4; + al2.allocate_l2(0x1000, 0x1000, &allocated4, &a4); + ASSERT_EQ(a4.size(), 0u); + } + } + + std::cout << "Done L2 Unaligned" << std::endl; +} + +TEST(TestAllocatorLevel01, test_l2_contiguous_alignment) +{ + { + TestAllocatorLevel02 al2; + uint64_t num_l2_entries = 3; + uint64_t capacity = num_l2_entries * 256 * 512 * 4096; // 3x512 MB + uint64_t num_chunks = capacity / 4096; + al2.init(capacity, 4096); + std::cout << "Init L2 cont aligned" << std::endl; + + std::map<size_t, size_t> bins_overall; + al2.collect_stats(bins_overall); + ASSERT_EQ(bins_overall.size(), 1u); +// std::cout<<bins_overall.begin()->first << std::endl; + ASSERT_EQ(bins_overall[cbits(num_chunks) - 1], 1u); + + for (uint64_t i = 0; i < capacity / 2; i += _1m) { + uint64_t allocated4 = 0; + interval_vector_t a4; + al2.allocate_l2(_1m, _1m, &allocated4, &a4); + ASSERT_EQ(a4.size(), 1u); + ASSERT_EQ(allocated4, _1m); + ASSERT_EQ(a4[0].offset, i); + ASSERT_EQ(a4[0].length, _1m); + } + ASSERT_EQ(capacity / 2, al2.debug_get_free()); + + bins_overall.clear(); + al2.collect_stats(bins_overall); + ASSERT_EQ(bins_overall.size(), 1u); + ASSERT_EQ(bins_overall[cbits(num_chunks / 2) - 1], 1u); + + { + size_t to_release = 2 * _1m + 0x1000; + // release 2M + 4K at the beginning + interval_vector_t r; + r.emplace_back(0, to_release); + al2.free_l2(r); + bins_overall.clear(); + al2.collect_stats(bins_overall); + ASSERT_EQ(bins_overall.size(), 2u); + ASSERT_EQ(bins_overall[cbits(to_release / 0x1000) - 1], 1u); + ASSERT_EQ(bins_overall[cbits(num_chunks / 2) - 1], 1u); + } + { + // allocate 4K within the deallocated range + uint64_t allocated4 = 0; + interval_vector_t a4; + al2.allocate_l2(0x1000, 0x1000, &allocated4, &a4); + ASSERT_EQ(a4.size(), 1u); + ASSERT_EQ(allocated4, 0x1000u); + ASSERT_EQ(a4[0].offset, 0u); + ASSERT_EQ(a4[0].length, 0x1000u); + bins_overall.clear(); + al2.collect_stats(bins_overall); + ASSERT_EQ(bins_overall.size(), 2u); + ASSERT_EQ(bins_overall[cbits(2 * _1m / 0x1000) - 1], 1u); + ASSERT_EQ(bins_overall[cbits(num_chunks / 2) - 1], 1u); + } + { + // allocate 1M - should go to the second 1M chunk + uint64_t allocated4 = 0; + interval_vector_t a4; + al2.allocate_l2(_1m, _1m, &allocated4, &a4); + ASSERT_EQ(a4.size(), 1u); + ASSERT_EQ(allocated4, _1m); + ASSERT_EQ(a4[0].offset, _1m); + ASSERT_EQ(a4[0].length, _1m); + bins_overall.clear(); + al2.collect_stats(bins_overall); + ASSERT_EQ(bins_overall.size(), 3u); + ASSERT_EQ(bins_overall[0], 1u); + ASSERT_EQ(bins_overall[cbits((_1m - 0x1000) / 0x1000) - 1], 1u); + ASSERT_EQ(bins_overall[cbits(num_chunks / 2) - 1], 1u); + } + { + // and allocate yet another 8K within the deallocated range + uint64_t allocated4 = 0; + interval_vector_t a4; + al2.allocate_l2(0x2000, 0x1000, &allocated4, &a4); + ASSERT_EQ(a4.size(), 1u); + ASSERT_EQ(allocated4, 0x2000u); + ASSERT_EQ(a4[0].offset, 0x1000u); + ASSERT_EQ(a4[0].length, 0x2000u); + bins_overall.clear(); + al2.collect_stats(bins_overall); + ASSERT_EQ(bins_overall[0], 1u); + ASSERT_EQ(bins_overall[cbits((_1m - 0x3000) / 0x1000) - 1], 1u); + ASSERT_EQ(bins_overall[cbits(num_chunks / 2) - 1], 1u); + } + { + // release just allocated 1M + interval_vector_t r; + r.emplace_back(_1m, _1m); + al2.free_l2(r); + bins_overall.clear(); + al2.collect_stats(bins_overall); + ASSERT_EQ(bins_overall.size(), 2u); + ASSERT_EQ(bins_overall[cbits((2 * _1m - 0x3000) / 0x1000) - 1], 1u); + ASSERT_EQ(bins_overall[cbits(num_chunks / 2) - 1], 1u); + } + { + // allocate 3M - should go to the second 1M chunk and @capacity/2 + uint64_t allocated4 = 0; + interval_vector_t a4; + al2.allocate_l2(3 * _1m, _1m, &allocated4, &a4); + ASSERT_EQ(a4.size(), 2u); + ASSERT_EQ(allocated4, 3 * _1m); + ASSERT_EQ(a4[0].offset, _1m); + ASSERT_EQ(a4[0].length, _1m); + ASSERT_EQ(a4[1].offset, capacity / 2); + ASSERT_EQ(a4[1].length, 2 * _1m); + bins_overall.clear(); + al2.collect_stats(bins_overall); + ASSERT_EQ(bins_overall.size(), 3u); + ASSERT_EQ(bins_overall[0], 1u); + ASSERT_EQ(bins_overall[cbits((_1m - 0x3000) / 0x1000) - 1], 1u); + ASSERT_EQ(bins_overall[cbits((num_chunks - 512) / 2) - 1], 1u); + } + { + // release allocated 1M in the second meg chunk except + // the first 4K chunk + interval_vector_t r; + r.emplace_back(_1m + 0x1000, _1m); + al2.free_l2(r); + bins_overall.clear(); + al2.collect_stats(bins_overall); + ASSERT_EQ(bins_overall.size(), 3u); + ASSERT_EQ(bins_overall[cbits(_1m / 0x1000) - 1], 1u); + ASSERT_EQ(bins_overall[cbits((_1m - 0x3000) / 0x1000) - 1], 1u); + ASSERT_EQ(bins_overall[cbits((num_chunks - 512) / 2) - 1], 1u); + } + { + // release 2M @(capacity / 2) + interval_vector_t r; + r.emplace_back(capacity / 2, 2 * _1m); + al2.free_l2(r); + bins_overall.clear(); + al2.collect_stats(bins_overall); + ASSERT_EQ(bins_overall.size(), 3u); + ASSERT_EQ(bins_overall[cbits(_1m / 0x1000) - 1], 1u); + ASSERT_EQ(bins_overall[cbits((_1m - 0x3000) / 0x1000) - 1], 1u); + ASSERT_EQ(bins_overall[cbits((num_chunks) / 2) - 1], 1u); + } + { + // allocate 4x512K - should go to the second halves of + // the first and second 1M chunks and @(capacity / 2) + uint64_t allocated4 = 0; + interval_vector_t a4; + al2.allocate_l2(2 * _1m, _1m / 2, &allocated4, &a4); + ASSERT_EQ(a4.size(), 3u); + ASSERT_EQ(allocated4, 2 * _1m); + ASSERT_EQ(a4[0].offset, _1m / 2); + ASSERT_EQ(a4[0].length, _1m / 2); + ASSERT_EQ(a4[1].offset, _1m + _1m / 2); + ASSERT_EQ(a4[1].length, _1m / 2); + ASSERT_EQ(a4[2].offset, capacity / 2); + ASSERT_EQ(a4[2].length, _1m); + + bins_overall.clear(); + al2.collect_stats(bins_overall); + ASSERT_EQ(bins_overall.size(), 3u); + ASSERT_EQ(bins_overall[0], 1u); + // below we have 512K - 4K & 512K - 12K chunks which both fit into + // the same bin = 6 + ASSERT_EQ(bins_overall[6], 2u); + ASSERT_EQ(bins_overall[cbits((num_chunks - 256) / 2) - 1], 1u); + + } + { + // cleanup first 2M except except the last 4K chunk + interval_vector_t r; + r.emplace_back(0, 2 * _1m - 0x1000); + al2.free_l2(r); + bins_overall.clear(); + al2.collect_stats(bins_overall); + + ASSERT_EQ(bins_overall.size(), 3u); + ASSERT_EQ(bins_overall[0], 1u); + ASSERT_EQ(bins_overall[cbits((_2m - 0x1000) / 0x1000) - 1], 1u); + ASSERT_EQ(bins_overall[cbits((num_chunks - 256) / 2) - 1], 1u); + } + { + // release 2M @(capacity / 2) + interval_vector_t r; + r.emplace_back(capacity / 2, 2 * _1m); + al2.free_l2(r); + bins_overall.clear(); + al2.collect_stats(bins_overall); + + ASSERT_EQ(bins_overall.size(), 3u); + ASSERT_EQ(bins_overall[0], 1u); + ASSERT_EQ(bins_overall[cbits((_2m - 0x1000) / 0x1000) - 1], 1u); + ASSERT_EQ(bins_overall[cbits(num_chunks / 2) - 1], 1u); + } + { + // allocate 132M using 4M granularity should go to (capacity / 2) + uint64_t allocated4 = 0; + interval_vector_t a4; + al2.allocate_l2(132 * _1m, 4 * _1m , &allocated4, &a4); + ASSERT_EQ(a4.size(), 1u); + ASSERT_EQ(a4[0].offset, capacity / 2); + ASSERT_EQ(a4[0].length, 132 * _1m); + + bins_overall.clear(); + al2.collect_stats(bins_overall); + ASSERT_EQ(bins_overall.size(), 3u); + } + { + // cleanup left 4K chunk in the first 2M + interval_vector_t r; + r.emplace_back(2 * _1m - 0x1000, 0x1000); + al2.free_l2(r); + bins_overall.clear(); + al2.collect_stats(bins_overall); + + ASSERT_EQ(bins_overall.size(), 2u); + } + { + // release 132M @(capacity / 2) + interval_vector_t r; + r.emplace_back(capacity / 2, 132 * _1m); + al2.free_l2(r); + } + { + // allocate 132M using 2M granularity should go to the first chunk and to + // (capacity / 2) + uint64_t allocated4 = 0; + interval_vector_t a4; + al2.allocate_l2(132 * _1m, 2 * _1m , &allocated4, &a4); + ASSERT_EQ(a4.size(), 2u); + ASSERT_EQ(a4[0].offset, 0u); + ASSERT_EQ(a4[0].length, 2 * _1m); + ASSERT_EQ(a4[1].offset, capacity / 2); + ASSERT_EQ(a4[1].length, 130 * _1m); + } + { + // release 130M @(capacity / 2) + interval_vector_t r; + r.emplace_back(capacity / 2, 132 * _1m); + al2.free_l2(r); + } + { + // release 4K~16K + // release 28K~32K + // release 68K~24K + interval_vector_t r; + r.emplace_back(0x1000, 0x4000); + r.emplace_back(0x7000, 0x8000); + r.emplace_back(0x11000, 0x6000); + al2.free_l2(r); + } + { + // allocate 32K using 16K granularity - should bypass the first + // unaligned extent, use the second free extent partially given + // the 16K alignment and then fallback to capacity / 2 + uint64_t allocated4 = 0; + interval_vector_t a4; + al2.allocate_l2(0x8000, 0x4000, &allocated4, &a4); + ASSERT_EQ(a4.size(), 2u); + ASSERT_EQ(a4[0].offset, 0x8000u); + ASSERT_EQ(a4[0].length, 0x4000u); + ASSERT_EQ(a4[1].offset, capacity / 2); + ASSERT_EQ(a4[1].length, 0x4000u); + } + + } + std::cout << "Done L2 cont aligned" << std::endl; +} + +TEST(TestAllocatorLevel01, test_4G_alloc_bug) +{ + { + TestAllocatorLevel02 al2; + uint64_t capacity = 0x8000 * _1m; // = 32GB + al2.init(capacity, 0x10000); + std::cout << "Init L2 cont aligned" << std::endl; + + uint64_t allocated4 = 0; + interval_vector_t a4; + al2.allocate_l2(_1m, _1m, &allocated4, &a4); + ASSERT_EQ(a4.size(), 1u); // the bug caused no allocations here + ASSERT_EQ(allocated4, _1m); + ASSERT_EQ(a4[0].offset, 0u); + ASSERT_EQ(a4[0].length, _1m); + } +} + +TEST(TestAllocatorLevel01, test_4G_alloc_bug2) +{ + { + TestAllocatorLevel02 al2; + uint64_t capacity = 0x8000 * _1m; // = 32GB + al2.init(capacity, 0x10000); + + for (uint64_t i = 0; i < capacity; i += _1m) { + uint64_t allocated4 = 0; + interval_vector_t a4; + al2.allocate_l2(_1m, _1m, &allocated4, &a4); + ASSERT_EQ(a4.size(), 1u); + ASSERT_EQ(allocated4, _1m); + ASSERT_EQ(a4[0].offset, i); + ASSERT_EQ(a4[0].length, _1m); + } + ASSERT_EQ(0u , al2.debug_get_free()); + + interval_vector_t r; + r.emplace_back(0x5fec30000, 0x13d0000); + r.emplace_back(0x628000000, 0x80000000); + r.emplace_back(0x6a8000000, 0x80000000); + r.emplace_back(0x728100000, 0x70000); + al2.free_l2(r); + + std::map<size_t, size_t> bins_overall; + al2.collect_stats(bins_overall); + + uint64_t allocated4 = 0; + interval_vector_t a4; + al2.allocate_l2(0x3e000000, _1m, &allocated4, &a4); + ASSERT_EQ(a4.size(), 2u); + ASSERT_EQ(allocated4, 0x3e000000u); + ASSERT_EQ(a4[0].offset, 0x5fed00000u); + ASSERT_EQ(a4[0].length, 0x1300000u); + ASSERT_EQ(a4[1].offset, 0x628000000u); + ASSERT_EQ(a4[1].length, 0x3cd00000u); + } +} + +TEST(TestAllocatorLevel01, test_4G_alloc_bug3) +{ + { + TestAllocatorLevel02 al2; + uint64_t capacity = 0x8000 * _1m; // = 32GB + al2.init(capacity, 0x10000); + std::cout << "Init L2 cont aligned" << std::endl; + + uint64_t allocated4 = 0; + interval_vector_t a4; + al2.allocate_l2(4096ull * _1m, _1m, &allocated4, &a4); + ASSERT_EQ(a4.size(), 2u); // allocator has to split into 2 allocations + ASSERT_EQ(allocated4, 4096ull * _1m); + ASSERT_EQ(a4[0].offset, 0u); + ASSERT_EQ(a4[0].length, 2048ull * _1m); + ASSERT_EQ(a4[1].offset, 2048ull * _1m); + ASSERT_EQ(a4[1].length, 2048ull * _1m); + } +} + +TEST(TestAllocatorLevel01, test_claim_free_l2) +{ + TestAllocatorLevel02 al2; + uint64_t num_l2_entries = 64;// *512; + uint64_t capacity = num_l2_entries * 256 * 512 * 4096; + al2.init(capacity, 0x1000); + std::cout << "Init L2" << std::endl; + + uint64_t max_available = 0x20000; + al2.mark_allocated(max_available, capacity - max_available); + + uint64_t allocated1 = 0; + interval_vector_t a1; + al2.allocate_l2(0x2000, 0x2000, &allocated1, &a1); + ASSERT_EQ(allocated1, 0x2000u); + ASSERT_EQ(a1[0].offset, 0u); + ASSERT_EQ(a1[0].length, 0x2000u); + + uint64_t allocated2 = 0; + interval_vector_t a2; + al2.allocate_l2(0x2000, 0x2000, &allocated2, &a2); + ASSERT_EQ(allocated2, 0x2000u); + ASSERT_EQ(a2[0].offset, 0x2000u); + ASSERT_EQ(a2[0].length, 0x2000u); + + uint64_t allocated3 = 0; + interval_vector_t a3; + al2.allocate_l2(0x3000, 0x3000, &allocated3, &a3); + ASSERT_EQ(allocated3, 0x3000u); + ASSERT_EQ(a3[0].offset, 0x4000u); + ASSERT_EQ(a3[0].length, 0x3000u); + + al2.free_l2(a1); + al2.free_l2(a3); + ASSERT_EQ(max_available - 0x2000, al2.debug_get_free()); + + auto claimed = al2.claim_free_to_right(0x4000); + ASSERT_EQ(max_available - 0x4000u, claimed); + ASSERT_EQ(0x2000, al2.debug_get_free()); + + claimed = al2.claim_free_to_right(0x4000); + ASSERT_EQ(0, claimed); + ASSERT_EQ(0x2000, al2.debug_get_free()); + + claimed = al2.claim_free_to_left(0x2000); + ASSERT_EQ(0x2000u, claimed); + ASSERT_EQ(0, al2.debug_get_free()); + + claimed = al2.claim_free_to_left(0x2000); + ASSERT_EQ(0, claimed); + ASSERT_EQ(0, al2.debug_get_free()); + + + al2.mark_free(0x3000, 0x4000); + ASSERT_EQ(0x4000, al2.debug_get_free()); + + claimed = al2.claim_free_to_right(0x7000); + ASSERT_EQ(0, claimed); + ASSERT_EQ(0x4000, al2.debug_get_free()); + + claimed = al2.claim_free_to_right(0x6000); + ASSERT_EQ(0x1000, claimed); + ASSERT_EQ(0x3000, al2.debug_get_free()); + + claimed = al2.claim_free_to_right(0x6000); + ASSERT_EQ(0, claimed); + ASSERT_EQ(0x3000, al2.debug_get_free()); + + claimed = al2.claim_free_to_left(0x3000); + ASSERT_EQ(0u, claimed); + ASSERT_EQ(0x3000, al2.debug_get_free()); + + claimed = al2.claim_free_to_left(0x4000); + ASSERT_EQ(0x1000, claimed); + ASSERT_EQ(0x2000, al2.debug_get_free()); + + // claiming on the right boundary + claimed = al2.claim_free_to_right(capacity); + ASSERT_EQ(0x0, claimed); + ASSERT_EQ(0x2000, al2.debug_get_free()); + + // extend allocator space up to 64M + auto max_available2 = 64 * 1024 * 1024; + al2.mark_free(max_available, max_available2 - max_available); + ASSERT_EQ(max_available2 - max_available + 0x2000, al2.debug_get_free()); + + // pin some allocations + al2.mark_allocated(0x400000 + 0x2000, 1000); + al2.mark_allocated(0x400000 + 0x5000, 1000); + al2.mark_allocated(0x400000 + 0x20000, 1000); + ASSERT_EQ(max_available2 - max_available - 0x1000, al2.debug_get_free()); + + claimed = al2.claim_free_to_left(0x403000); + ASSERT_EQ(0x0, claimed); + + claimed = al2.claim_free_to_left(0x404000); + ASSERT_EQ(0x1000, claimed); + ASSERT_EQ(max_available2 - max_available - 0x2000, al2.debug_get_free()); + + claimed = al2.claim_free_to_left(max_available); + ASSERT_EQ(0, claimed); + + claimed = al2.claim_free_to_left(0x400000); + ASSERT_EQ(0x3e0000, claimed); + ASSERT_EQ(max_available2 - max_available - 0x3e2000, al2.get_available()); + ASSERT_EQ(max_available2 - max_available - 0x3e2000, al2.debug_get_free()); + + claimed = al2.claim_free_to_right(0x407000); + ASSERT_EQ(0x19000, claimed); + ASSERT_EQ(max_available2 - max_available - 0x3e2000 - 0x19000, + al2.get_available()); + ASSERT_EQ(max_available2 - max_available - 0x3e2000 - 0x19000, + al2.debug_get_free()); + + claimed = al2.claim_free_to_right(0x407000); + ASSERT_EQ(0, claimed); + + claimed = al2.claim_free_to_right(0x430000); + ASSERT_EQ(max_available2 - 0x430000, claimed); + ASSERT_EQ(0x15000, + al2.get_available()); + ASSERT_EQ(0x15000, + al2.debug_get_free()); +} |