summaryrefslogtreecommitdiffstats
path: root/src/test/objectstore/Allocator_bench.cc
diff options
context:
space:
mode:
Diffstat (limited to 'src/test/objectstore/Allocator_bench.cc')
-rw-r--r--src/test/objectstore/Allocator_bench.cc364
1 files changed, 364 insertions, 0 deletions
diff --git a/src/test/objectstore/Allocator_bench.cc b/src/test/objectstore/Allocator_bench.cc
new file mode 100644
index 000000000..71277bc80
--- /dev/null
+++ b/src/test/objectstore/Allocator_bench.cc
@@ -0,0 +1,364 @@
+// -*- 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 <iostream>
+#include <boost/scoped_ptr.hpp>
+#include <gtest/gtest.h>
+
+#include "common/Cond.h"
+#include "common/errno.h"
+#include "include/stringify.h"
+#include "include/Context.h"
+#include "os/bluestore/Allocator.h"
+
+#include <boost/random/uniform_int.hpp>
+typedef boost::mt11213b gen_type;
+
+#include "common/debug.h"
+#define dout_context g_ceph_context
+#define dout_subsys ceph_subsys_
+
+class AllocTest : public ::testing::TestWithParam<const char*> {
+
+public:
+ boost::scoped_ptr<Allocator> 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;
+
+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<uint64_t> 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(0, capacity)
+ {
+ ceph_assert(alloc_unit >= 0x100);
+ ceph_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)
+ {
+ ceph_assert((len & 0xff) == 0);
+ ceph_assert((offs & 0xff) == 0);
+ ceph_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(static_cast<int64_t>(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<uint64_t> 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<uint64_t> 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<uint64_t> 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);
+}
+
+TEST_P(AllocTest, mempoolAccounting)
+{
+ uint64_t bytes = mempool::bluestore_alloc::allocated_bytes();
+ uint64_t items = mempool::bluestore_alloc::allocated_items();
+
+ uint64_t alloc_size = 4 * 1024;
+ uint64_t capacity = 512ll * 1024 * 1024 * 1024;
+ Allocator* alloc = Allocator::create(g_ceph_context, string(GetParam()),
+ capacity, alloc_size);
+ ASSERT_NE(alloc, nullptr);
+ alloc->init_add_free(0, capacity);
+
+ std::map<uint32_t, PExtentVector> all_allocs;
+ for (size_t i = 0; i < 10000; i++) {
+ PExtentVector tmp;
+ alloc->allocate(alloc_size, alloc_size, 0, 0, &tmp);
+ all_allocs[rand()] = tmp;
+ alloc->allocate(alloc_size, alloc_size, 0, 0, &tmp);
+ all_allocs[rand()] = tmp;
+
+ auto it = all_allocs.upper_bound(rand());
+ if (it != all_allocs.end()) {
+ alloc->release(it->second);
+ all_allocs.erase(it);
+ }
+ }
+
+ delete(alloc);
+ ASSERT_EQ(mempool::bluestore_alloc::allocated_bytes(), bytes);
+ ASSERT_EQ(mempool::bluestore_alloc::allocated_items(), items);
+}
+
+INSTANTIATE_TEST_SUITE_P(
+ Allocator,
+ AllocTest,
+ ::testing::Values("stupid", "bitmap", "avl", "hybrid"));