summaryrefslogtreecommitdiffstats
path: root/src/seastar/tests/unit/allocator_test.cc
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-27 18:24:20 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-27 18:24:20 +0000
commit483eb2f56657e8e7f419ab1a4fab8dce9ade8609 (patch)
treee5d88d25d870d5dedacb6bbdbe2a966086a0a5cf /src/seastar/tests/unit/allocator_test.cc
parentInitial commit. (diff)
downloadceph-upstream.tar.xz
ceph-upstream.zip
Adding upstream version 14.2.21.upstream/14.2.21upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/seastar/tests/unit/allocator_test.cc')
-rw-r--r--src/seastar/tests/unit/allocator_test.cc235
1 files changed, 235 insertions, 0 deletions
diff --git a/src/seastar/tests/unit/allocator_test.cc b/src/seastar/tests/unit/allocator_test.cc
new file mode 100644
index 00000000..c0d93210
--- /dev/null
+++ b/src/seastar/tests/unit/allocator_test.cc
@@ -0,0 +1,235 @@
+/*
+ * This file is open source software, licensed to you under the terms
+ * of the Apache License, Version 2.0 (the "License"). See the NOTICE file
+ * distributed with this work for additional information regarding copyright
+ * ownership. You may not use this file except in compliance with the License.
+ *
+ * You may obtain a copy of the License at
+ *
+ * http://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.
+ */
+/*
+ * Copyright 2014 Cloudius Systems
+ */
+
+#include <seastar/core/memory.hh>
+#include <seastar/core/timer.hh>
+#include <random>
+#include <cmath>
+#include <iostream>
+#include <iomanip>
+#include <algorithm>
+#include <cassert>
+#include <memory>
+#include <chrono>
+#include <boost/program_options.hpp>
+
+using namespace seastar;
+
+template <size_t N>
+void test_aligned_allocator() {
+#ifdef __cpp_aligned_new
+ using aptr = std::unique_ptr<char[]>;
+ std::vector<aptr> v;
+ for (unsigned i = 0; i < 1000; ++i) {
+ aptr p(new (std::align_val_t(64)) char[N]);
+ assert(reinterpret_cast<uintptr_t>(p.get()) % 64 == 0);
+ v.push_back(std::move(p));
+ }
+#endif
+}
+
+struct allocation {
+ size_t n;
+ std::unique_ptr<char[]> data;
+ char poison;
+ allocation(size_t n, char poison) : n(n), data(new char[n]), poison(poison) {
+ std::fill_n(data.get(), n, poison);
+ }
+ ~allocation() {
+ verify();
+ }
+ allocation(allocation&& x) noexcept = default;
+ void verify() {
+ if (data) {
+ assert(std::find_if(data.get(), data.get() + n, [this] (char c) {
+ return c != poison;
+ }) == data.get() + n);
+ }
+ }
+ allocation& operator=(allocation&& x) {
+ verify();
+ if (this != &x) {
+ data = std::move(x.data);
+ n = x.n;
+ poison = x.poison;
+ }
+ return *this;
+ }
+};
+
+#ifdef __cpp_aligned_new
+
+template <size_t N>
+struct alignas(N) cpp17_allocation final {
+ char v;
+};
+
+struct test17 {
+ struct handle {
+ const test17* d;
+ void* p;
+ handle(const test17* d, void* p) : d(d), p(p) {}
+ handle(const handle&) = delete;
+ handle(handle&& x) noexcept : d(std::exchange(x.d, nullptr)), p(std::exchange(x.p, nullptr)) {}
+ handle& operator=(const handle&) = delete;
+ handle& operator=(handle&& x) noexcept {
+ std::swap(d, x.d);
+ std::swap(p, x.p);
+ return *this;
+ }
+ ~handle() {
+ if (d) {
+ d->free(p);
+ }
+ }
+ };
+ virtual ~test17() {}
+ virtual handle alloc() const = 0;
+ virtual void free(void* ptr) const = 0;
+};
+
+template <size_t N>
+struct test17_concrete : test17 {
+ using value_type = cpp17_allocation<N>;
+ static_assert(sizeof(value_type) == N, "language does not guarantee size >= align");
+ virtual handle alloc() const override {
+ auto ptr = new value_type();
+ assert((reinterpret_cast<uintptr_t>(ptr) & (N - 1)) == 0);
+ return handle{this, ptr};
+ }
+ virtual void free(void* ptr) const override {
+ delete static_cast<value_type*>(ptr);
+ }
+};
+
+void test_cpp17_aligned_allocator() {
+ std::vector<std::unique_ptr<test17>> tv;
+ tv.push_back(std::make_unique<test17_concrete<1>>());
+ tv.push_back(std::make_unique<test17_concrete<2>>());
+ tv.push_back(std::make_unique<test17_concrete<4>>());
+ tv.push_back(std::make_unique<test17_concrete<8>>());
+ tv.push_back(std::make_unique<test17_concrete<16>>());
+ tv.push_back(std::make_unique<test17_concrete<64>>());
+ tv.push_back(std::make_unique<test17_concrete<128>>());
+ tv.push_back(std::make_unique<test17_concrete<2048>>());
+ tv.push_back(std::make_unique<test17_concrete<4096>>());
+ tv.push_back(std::make_unique<test17_concrete<4096*16>>());
+ tv.push_back(std::make_unique<test17_concrete<4096*256>>());
+
+ std::default_random_engine random_engine;
+ std::uniform_int_distribution<> type_dist(0, 1);
+ std::uniform_int_distribution<size_t> size_dist(0, tv.size() - 1);
+ std::uniform_real_distribution<> which_dist(0, 1);
+
+ std::vector<test17::handle> allocs;
+ for (unsigned i = 0; i < 10000; ++i) {
+ auto type = type_dist(random_engine);
+ switch (type) {
+ case 0: {
+ size_t sz_idx = size_dist(random_engine);
+ allocs.push_back(tv[sz_idx]->alloc());
+ break;
+ }
+ case 1:
+ if (!allocs.empty()) {
+ size_t idx = which_dist(random_engine) * allocs.size();
+ std::swap(allocs[idx], allocs.back());
+ allocs.pop_back();
+ }
+ break;
+ }
+ }
+}
+
+#else
+
+void test_cpp17_aligned_allocator() {
+}
+
+#endif
+
+int main(int ac, char** av) {
+ namespace bpo = boost::program_options;
+ bpo::options_description opts("Allowed options");
+ opts.add_options()
+ ("help", "produce this help message")
+ ("iterations", bpo::value<unsigned>(), "run s specified number of iterations")
+ ("time", bpo::value<float>()->default_value(5.0), "run for a specified amount of time, in seconds")
+ ;
+ bpo::variables_map vm;
+ bpo::store(bpo::parse_command_line(ac, av, opts), vm);
+ bpo::notify(vm);
+ test_aligned_allocator<1>();
+ test_aligned_allocator<4>();
+ test_aligned_allocator<80>();
+ test_cpp17_aligned_allocator();
+ std::default_random_engine random_engine;
+ std::exponential_distribution<> distr(0.2);
+ std::uniform_int_distribution<> type(0, 1);
+ std::uniform_int_distribution<char> poison(-128, 127);
+ std::uniform_real_distribution<> which(0, 1);
+ std::vector<allocation> allocations;
+ auto iteration = [&] {
+ auto typ = type(random_engine);
+ switch (typ) {
+ case 0: {
+ auto n = std::min<size_t>(std::exp(distr(random_engine)), 1 << 25);
+ try {
+ allocations.emplace_back(n, poison(random_engine));
+ } catch (std::bad_alloc&) {
+
+ }
+ break;
+ }
+ case 1: {
+ if (allocations.empty()) {
+ break;
+ }
+ size_t i = which(random_engine) * allocations.size();
+ allocations[i] = std::move(allocations.back());
+ allocations.pop_back();
+ break;
+ }
+ }
+ };
+ if (vm.count("help")) {
+ std::cout << opts << "\n";
+ return 1;
+ }
+ if (vm.count("iterations")) {
+ auto iterations = vm["iterations"].as<unsigned>();
+ for (unsigned i = 0; i < iterations; ++i) {
+ iteration();
+ }
+ } else {
+ auto time = vm["time"].as<float>();
+ using clock = steady_clock_type;
+ auto end = clock::now() + std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::seconds(1) * time);
+ while (clock::now() < end) {
+ for (unsigned i = 0; i < 1000; ++i) {
+ iteration();
+ }
+ }
+ }
+ return 0;
+}
+
+