From ba5c402d762ce5766fcd0f1996461bd1e1024410 Mon Sep 17 00:00:00 2001 From: Igor Fedotov Date: Fri, 4 May 2018 20:03:37 +0300 Subject: [PATCH] test/allocator: move bluestore allocator's benchmarks to a standalone UT Signed-off-by: Igor Fedotov (cherry picked from commit ac2ec80cddc51f1642849c7bf226e9bed116c0d6) --- src/test/objectstore/Allocator_bench.cc | 340 ++++++++++++++++++++++++ src/test/objectstore/Allocator_test.cc | 189 +------------ src/test/objectstore/CMakeLists.txt | 8 + 3 files changed, 359 insertions(+), 178 deletions(-) create mode 100755 src/test/objectstore/Allocator_bench.cc diff --git a/src/test/objectstore/Allocator_bench.cc b/src/test/objectstore/Allocator_bench.cc new file mode 100755 index 0000000000000..bdd64800bb776 --- /dev/null +++ b/src/test/objectstore/Allocator_bench.cc @@ -0,0 +1,340 @@ +// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- +// vim: ts=8 sw=2 smarttab +/* + * In memory space allocator benchmarks. + * Author: Igor Fedotov, ifedotov@suse.com + */ +#include +#include +#include + +#include "common/Mutex.h" +#include "common/Cond.h" +#include "common/errno.h" +#include "include/stringify.h" +#include "include/Context.h" +#include "os/bluestore/Allocator.h" + +#include +typedef boost::mt11213b gen_type; + +#include "common/debug.h" +#define dout_context g_ceph_context +#define dout_subsys ceph_subsys_ + +#if GTEST_HAS_PARAM_TEST + +class AllocTest : public ::testing::TestWithParam { + +public: + boost::scoped_ptr alloc; + AllocTest(): alloc(0) { } + void init_alloc(int64_t size, uint64_t min_alloc_size) { + std::cout << "Creating alloc type " << string(GetParam()) << " \n"; + alloc.reset(Allocator::create(g_ceph_context, string(GetParam()), size, + min_alloc_size)); + } + + void init_close() { + alloc.reset(0); + } + void doOverwriteTest(uint64_t capacity, uint64_t prefill, + uint64_t overwrite); +}; + +const uint64_t _1m = 1024 * 1024; +const uint64_t _2m = 2 * 1024 * 1024; + +void dump_mempools() +{ + ostringstream ostr; + Formatter* f = Formatter::create("json-pretty", "json-pretty", "json-pretty"); + ostr << "Mempools: "; + f->open_object_section("mempools"); + mempool::dump(f); + f->close_section(); + f->flush(ostr); + delete f; + ldout(g_ceph_context, 0) << ostr.str() << dendl; +} + +class AllocTracker +{ + std::vector allocations; + uint64_t head = 0; + uint64_t tail = 0; + uint64_t size = 0; + boost::uniform_int<> u1; + +public: + AllocTracker(uint64_t capacity, uint64_t alloc_unit) + : u1(capacity, alloc_unit) + { + assert(alloc_unit >= 0x100); + assert(capacity <= (uint64_t(1) << 48)); // we use 5 octets (bytes 1 - 5) to store + // offset to save the required space. + // This supports capacity up to 281 TB + + allocations.resize(capacity / alloc_unit); + } + inline uint64_t get_head() const + { + return head; + } + + inline uint64_t get_tail() const + { + return tail; + } + + bool push(uint64_t offs, uint32_t len) + { + assert((len & 0xff) == 0); + assert((offs & 0xff) == 0); + assert((offs & 0xffff000000000000) == 0); + + if (head + 1 == tail) + return false; + uint64_t val = (offs << 16) | (len >> 8); + allocations[head++] = val; + head %= allocations.size(); + ++size; + return true; + } + bool pop(uint64_t* offs, uint32_t* len) + { + if (size == 0) + return false; + uint64_t val = allocations[tail++]; + *len = uint64_t((val & 0xffffff) << 8); + *offs = (val >> 16) & ~uint64_t(0xff); + tail %= allocations.size(); + --size; + return true; + } + bool pop_random(gen_type& rng, uint64_t* offs, uint32_t* len, + uint32_t max_len = 0) + { + if (size == 0) + return false; + + uint64_t pos = (u1(rng) % size) + tail; + pos %= allocations.size(); + uint64_t val = allocations[pos]; + *len = uint64_t((val & 0xffffff) << 8); + *offs = (val >> 16) & ~uint64_t(0xff); + if (max_len && *len > max_len) { + val = ((*offs + max_len) << 16) | ((*len - max_len) >> 8); + allocations[pos] = val; + *len = max_len; + } else { + allocations[pos] = allocations[tail++]; + tail %= allocations.size(); + --size; + } + return true; + } +}; + +TEST_P(AllocTest, test_alloc_bench_seq) +{ + uint64_t capacity = uint64_t(1024) * 1024 * 1024 * 1024; + uint64_t alloc_unit = 4096; + uint64_t want_size = alloc_unit; + PExtentVector allocated, tmp; + + init_alloc(capacity, alloc_unit); + alloc->init_add_free(0, capacity); + + utime_t start = ceph_clock_now(); + for (uint64_t i = 0; i < capacity; i += want_size) + { + tmp.clear(); + EXPECT_EQ(want_size, alloc->allocate(want_size, alloc_unit, 0, 0, &tmp)); + if (0 == (i % (1 * 1024 * _1m))) { + std::cout << "alloc " << i / 1024 / 1024 << " mb of " + << capacity / 1024 / 1024 << std::endl; + } + } + + std::cout << "releasing..." << std::endl; + for (size_t i = 0; i < capacity; i += want_size) + { + interval_set release_set; + release_set.insert(i, want_size); + alloc->release(release_set); + if (0 == (i % (1 * 1024 * _1m))) { + std::cout << "release " << i / 1024 / 1024 << " mb of " + << capacity / 1024 / 1024 << std::endl; + } + } + std::cout<<"Executed in "<< ceph_clock_now() - start << std::endl; + dump_mempools(); +} + +TEST_P(AllocTest, test_alloc_bench) +{ + uint64_t capacity = uint64_t(1024) * 1024 * 1024 * 1024; + uint64_t alloc_unit = 4096; + PExtentVector allocated, tmp; + AllocTracker at(capacity, alloc_unit); + + init_alloc(capacity, alloc_unit); + alloc->init_add_free(0, capacity); + + gen_type rng(time(NULL)); + boost::uniform_int<> u1(0, 9); // 4K-2M + boost::uniform_int<> u2(0, 7); // 4K-512K + + utime_t start = ceph_clock_now(); + for (uint64_t i = 0; i < capacity * 2; ) + { + uint32_t want = alloc_unit << u1(rng); + + tmp.clear(); + auto r = alloc->allocate(want, alloc_unit, 0, 0, &tmp); + if (r < want) { + break; + } + i += r; + + for(auto a : tmp) { + bool full = !at.push(a.offset, a.length); + EXPECT_EQ(full, false); + } + uint64_t want_release = alloc_unit << u2(rng); + uint64_t released = 0; + do { + uint64_t o = 0; + uint32_t l = 0; + interval_set release_set; + if (!at.pop_random(rng, &o, &l, want_release - released)) { + break; + } + release_set.insert(o, l); + alloc->release(release_set); + released += l; + } while (released < want_release); + + if (0 == (i % (1 * 1024 * _1m))) { + std::cout << "alloc " << i / 1024 / 1024 << " mb of " + << capacity / 1024 / 1024 << std::endl; + } + } + std::cout<<"Executed in "<< ceph_clock_now() - start << std::endl; + std::cout<<"Avail "<< alloc->get_free() / _1m << " MB" << std::endl; + dump_mempools(); +} + +void AllocTest::doOverwriteTest(uint64_t capacity, uint64_t prefill, + uint64_t overwrite) +{ + uint64_t alloc_unit = 4096; + PExtentVector allocated, tmp; + AllocTracker at(capacity, alloc_unit); + + init_alloc(capacity, alloc_unit); + alloc->init_add_free(0, capacity); + + gen_type rng(time(NULL)); + boost::uniform_int<> u1(0, 9); // 4K-2M + boost::uniform_int<> u2(0, 9); // 4K-512K + + utime_t start = ceph_clock_now(); + // allocate 90% of the capacity + auto cap = prefill; + for (uint64_t i = 0; i < cap; ) + { + uint32_t want = alloc_unit << u1(rng); + tmp.clear(); + auto r = alloc->allocate(want, alloc_unit, 0, 0, &tmp); + if (r < want) { + break; + } + i += r; + + for(auto a : tmp) { + bool full = !at.push(a.offset, a.length); + EXPECT_EQ(full, false); + } + if (0 == (i % (1 * 1024 * _1m))) { + std::cout << "alloc " << i / 1024 / 1024 << " mb of " + << cap / 1024 / 1024 << std::endl; + } + } + + cap = overwrite; + for (uint64_t i = 0; i < cap; ) + { + uint64_t want_release = alloc_unit << u2(rng); + uint64_t released = 0; + do { + uint64_t o = 0; + uint32_t l = 0; + interval_set release_set; + if (!at.pop_random(rng, &o, &l, want_release - released)) { + break; + } + release_set.insert(o, l); + alloc->release(release_set); + released += l; + } while (released < want_release); + + uint32_t want = alloc_unit << u1(rng); + tmp.clear(); + auto r = alloc->allocate(want, alloc_unit, 0, 0, &tmp); + if (r != want) { + std::cout<<"Can't allocate more space, stopping."<< std::endl; + break; + } + i += r; + + for(auto a : tmp) { + bool full = !at.push(a.offset, a.length); + EXPECT_EQ(full, false); + } + + if (0 == (i % (1 * 1024 * _1m))) { + std::cout << "reuse " << i / 1024 / 1024 << " mb of " + << cap / 1024 / 1024 << std::endl; + } + } + std::cout<<"Executed in "<< ceph_clock_now() - start << std::endl; + std::cout<<"Avail "<< alloc->get_free() / _1m << " MB" << std::endl; + + dump_mempools(); +} + +TEST_P(AllocTest, test_alloc_bench_90_300) +{ + uint64_t capacity = uint64_t(1024) * 1024 * 1024 * 1024; + auto prefill = capacity - capacity / 10; + auto overwrite = capacity * 3; + doOverwriteTest(capacity, prefill, overwrite); +} + +TEST_P(AllocTest, test_alloc_bench_50_300) +{ + uint64_t capacity = uint64_t(1024) * 1024 * 1024 * 1024; + auto prefill = capacity / 2; + auto overwrite = capacity * 3; + doOverwriteTest(capacity, prefill, overwrite); +} + +TEST_P(AllocTest, test_alloc_bench_10_300) +{ + uint64_t capacity = uint64_t(1024) * 1024 * 1024 * 1024; + auto prefill = capacity / 10; + auto overwrite = capacity * 3; + doOverwriteTest(capacity, prefill, overwrite); +} + +INSTANTIATE_TEST_CASE_P( + Allocator, + AllocTest, + ::testing::Values("stupid", "bitmap")); + +#else + +TEST(DummyTest, ValueParameterizedTestsAreNotSupportedOnThisPlatform) {} +#endif diff --git a/src/test/objectstore/Allocator_test.cc b/src/test/objectstore/Allocator_test.cc index 3e4f0bb095afd..5ff3aad7c59ea 100644 --- a/src/test/objectstore/Allocator_test.cc +++ b/src/test/objectstore/Allocator_test.cc @@ -21,18 +21,19 @@ typedef boost::mt11213b gen_type; #if GTEST_HAS_PARAM_TEST class AllocTest : public ::testing::TestWithParam { + public: - boost::scoped_ptr alloc; - AllocTest(): alloc(0) { } - void init_alloc(int64_t size, uint64_t min_alloc_size) { - std::cout << "Creating alloc type " << string(GetParam()) << " \n"; - alloc.reset(Allocator::create(g_ceph_context, string(GetParam()), size, - min_alloc_size)); - } + boost::scoped_ptr alloc; + AllocTest(): alloc(0) { } + void init_alloc(int64_t size, uint64_t min_alloc_size) { + std::cout << "Creating alloc type " << string(GetParam()) << " \n"; + alloc.reset(Allocator::create(g_ceph_context, string(GetParam()), size, + min_alloc_size)); + } - void init_close() { - alloc.reset(0); - } + void init_close() { + alloc.reset(0); + } }; TEST_P(AllocTest, test_alloc_init) @@ -276,174 +277,6 @@ TEST_P(AllocTest, test_alloc_fragmentation) EXPECT_EQ(0, uint64_t(alloc->get_fragmentation(alloc_unit) * 100)); } -const uint64_t _1m = 1024 * 1024; -const uint64_t _2m = 2 * 1024 * 1024; - -TEST_P(AllocTest, test_alloc_bench_seq) -{ - uint64_t capacity = uint64_t(1024) * 1024 * 1024 * 1024; - uint64_t alloc_unit = 4096; - uint64_t want_size = alloc_unit; - PExtentVector allocated, tmp; - - init_alloc(capacity, alloc_unit); - alloc->init_add_free(0, capacity); - - utime_t start = ceph_clock_now(); - for (uint64_t i = 0; i < capacity; i += want_size) - { - tmp.clear(); - EXPECT_EQ(want_size, alloc->allocate(want_size, alloc_unit, 0, 0, &tmp)); - if (0 == (i % (1 * 1024 * _1m))) { - std::cout << "alloc " << i / 1024 / 1024 << " mb of " - << capacity / 1024 / 1024 << std::endl; - } - } - - std::cout << "releasing..." << std::endl; - for (size_t i = 0; i < capacity; i += want_size) - { - interval_set release_set; - release_set.insert(i, want_size); - alloc->release(release_set); - if (0 == (i % (1 * 1024 * _1m))) { - std::cout << "release " << i / 1024 / 1024 << " mb of " - << capacity / 1024 / 1024 << std::endl; - } - } - std::cout<<"Executed in "<< ceph_clock_now() - start << std::endl; -} - -class AllocTracker -{ - std::vector allocations; - uint64_t head = 0; - uint64_t tail = 0; - uint64_t size = 0; - boost::uniform_int<> u1; - -public: - AllocTracker(uint64_t capacity, uint64_t alloc_unit) - : u1(capacity, alloc_unit) - { - assert(alloc_unit >= 0x100); - assert(capacity <= (uint64_t(1) << 48)); // we use 5 octets (bytes 1 - 5) to store - // offset to save the required space. - // This supports capacity up to 281 TB - - allocations.resize(capacity / alloc_unit); - } - inline uint64_t get_head() const - { - return head; - } - - inline uint64_t get_tail() const - { - return tail; - } - - bool push(uint64_t offs, uint32_t len) - { - assert((len & 0xff) == 0); - assert((offs & 0xff) == 0); - assert((offs & 0xffff000000000000) == 0); - - if (head + 1 == tail) - return false; - uint64_t val = (offs << 16) | (len >> 8); - allocations[head++] = val; - head %= allocations.size(); - ++size; - return true; - } - bool pop(uint64_t* offs, uint32_t* len) - { - if (size == 0) - return false; - uint64_t val = allocations[tail++]; - *len = uint64_t((val & 0xffffff) << 8); - *offs = (val >> 16) & ~uint64_t(0xff); - tail %= allocations.size(); - --size; - return true; - } - bool pop_random(gen_type& rng, uint64_t* offs, uint32_t* len, - uint32_t max_len = 0) - { - if (size == 0) - return false; - - uint64_t pos = (u1(rng) % size) + tail; - pos %= allocations.size(); - uint64_t val = allocations[pos]; - *len = uint64_t((val & 0xffffff) << 8); - *offs = (val >> 16) & ~uint64_t(0xff); - if (max_len && *len > max_len) { - val = ((*offs + max_len) << 16) | ((*len - max_len) >> 8); - allocations[pos] = val; - *len = max_len; - } else { - allocations[pos] = allocations[tail++]; - tail %= allocations.size(); - --size; - } - return true; - } -}; - -TEST_P(AllocTest, test_alloc_bench) -{ - uint64_t capacity = uint64_t(1024) * 1024 * 1024 * 1024; - uint64_t alloc_unit = 4096; - PExtentVector allocated, tmp; - AllocTracker at(capacity, alloc_unit); - - init_alloc(capacity, alloc_unit); - alloc->init_add_free(0, capacity); - - gen_type rng(time(NULL)); - boost::uniform_int<> u1(0, 9); // 4K-2M - boost::uniform_int<> u2(0, 7); // 4K-512K - - utime_t start = ceph_clock_now(); - for (uint64_t i = 0; i < capacity * 2; ) - { - uint32_t want = alloc_unit << u1(rng); - auto r = alloc->allocate(want, alloc_unit, 0, 0, &tmp); - if (r <= 0) { - break; - } - i += want; - tmp.clear(); - - for(auto a : tmp) { - bool full = !at.push(a.offset, a.length); - EXPECT_EQ(full, false); - } - uint64_t want_release = alloc_unit << u2(rng); - uint64_t released = 0; - do { - uint64_t o = 0; - uint32_t l = 0; - interval_set release_set; - if (!at.pop_random(rng, &o, &l, want_release - released)) { - break; - } - release_set.insert(o, l); - alloc->release(release_set); - released += l; - } while (released < want_release); - - if (0 == (i % (1 * 1024 * _1m))) { - std::cout << "alloc " << i / 1024 / 1024 << " mb of " - << capacity / 1024 / 1024 << std::endl; - } - } - std::cout<<"Executed in "<< ceph_clock_now() - start << std::endl; - std::cout<<"Avail "<< alloc->get_free() / _1m << " MB" << std::endl; -} - INSTANTIATE_TEST_CASE_P( Allocator, AllocTest, diff --git a/src/test/objectstore/CMakeLists.txt b/src/test/objectstore/CMakeLists.txt index e2666870a703a..cf2fb8a4f0abe 100644 --- a/src/test/objectstore/CMakeLists.txt +++ b/src/test/objectstore/CMakeLists.txt @@ -111,6 +111,14 @@ if(WITH_BLUESTORE) add_ceph_unittest(unittest_alloc) target_link_libraries(unittest_alloc os global) + + add_executable(unittest_alloc_bench + Allocator_bench.cc + $ + ) + add_ceph_unittest(unittest_alloc_bench) + target_link_libraries(unittest_alloc_bench os global) + add_executable(unittest_fastbmap_allocator fastbmap_allocator_test.cc $ -- 2.39.5