]> git.proxmox.com Git - ceph.git/blob - ceph/src/seastar/tests/unit/allocator_test.cc
update source to Ceph Pacific 16.2.2
[ceph.git] / ceph / src / seastar / tests / unit / allocator_test.cc
1 /*
2 * This file is open source software, licensed to you under the terms
3 * of the Apache License, Version 2.0 (the "License"). See the NOTICE file
4 * distributed with this work for additional information regarding copyright
5 * ownership. You may not use this file except in compliance with the License.
6 *
7 * You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing,
12 * software distributed under the License is distributed on an
13 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14 * KIND, either express or implied. See the License for the
15 * specific language governing permissions and limitations
16 * under the License.
17 */
18 /*
19 * Copyright 2014 Cloudius Systems
20 */
21
22 #include <seastar/core/memory.hh>
23 #include <seastar/core/timer.hh>
24 #include <seastar/testing/test_runner.hh>
25 #include <cmath>
26 #include <iostream>
27 #include <iomanip>
28 #include <algorithm>
29 #include <cassert>
30 #include <memory>
31 #include <chrono>
32 #include <boost/program_options.hpp>
33
34 using namespace seastar;
35
36 struct allocation {
37 size_t n;
38 std::unique_ptr<char[]> data;
39 char poison;
40 allocation(size_t n, char poison) : n(n), data(new char[n]), poison(poison) {
41 std::fill_n(data.get(), n, poison);
42 }
43 ~allocation() {
44 verify();
45 }
46 allocation(allocation&& x) noexcept = default;
47 void verify() {
48 if (data) {
49 assert(std::find_if(data.get(), data.get() + n, [this] (char c) {
50 return c != poison;
51 }) == data.get() + n);
52 }
53 }
54 allocation& operator=(allocation&& x) {
55 verify();
56 if (this != &x) {
57 data = std::move(x.data);
58 n = x.n;
59 poison = x.poison;
60 }
61 return *this;
62 }
63 };
64
65 #ifdef __cpp_aligned_new
66
67 template <size_t N>
68 struct alignas(N) cpp17_allocation final {
69 char v;
70 };
71
72 struct test17 {
73 struct handle {
74 const test17* d;
75 void* p;
76 handle(const test17* d, void* p) : d(d), p(p) {}
77 handle(const handle&) = delete;
78 handle(handle&& x) noexcept : d(std::exchange(x.d, nullptr)), p(std::exchange(x.p, nullptr)) {}
79 handle& operator=(const handle&) = delete;
80 handle& operator=(handle&& x) noexcept {
81 std::swap(d, x.d);
82 std::swap(p, x.p);
83 return *this;
84 }
85 ~handle() {
86 if (d) {
87 d->free(p);
88 }
89 }
90 };
91 virtual ~test17() {}
92 virtual handle alloc() const = 0;
93 virtual void free(void* ptr) const = 0;
94 };
95
96 template <size_t N>
97 struct test17_concrete : test17 {
98 using value_type = cpp17_allocation<N>;
99 static_assert(sizeof(value_type) == N, "language does not guarantee size >= align");
100 virtual handle alloc() const override {
101 auto ptr = new value_type();
102 assert((reinterpret_cast<uintptr_t>(ptr) & (N - 1)) == 0);
103 return handle{this, ptr};
104 }
105 virtual void free(void* ptr) const override {
106 delete static_cast<value_type*>(ptr);
107 }
108 };
109
110 void test_cpp17_aligned_allocator() {
111 std::vector<std::unique_ptr<test17>> tv;
112 tv.push_back(std::make_unique<test17_concrete<1>>());
113 tv.push_back(std::make_unique<test17_concrete<2>>());
114 tv.push_back(std::make_unique<test17_concrete<4>>());
115 tv.push_back(std::make_unique<test17_concrete<8>>());
116 tv.push_back(std::make_unique<test17_concrete<16>>());
117 tv.push_back(std::make_unique<test17_concrete<64>>());
118 tv.push_back(std::make_unique<test17_concrete<128>>());
119 tv.push_back(std::make_unique<test17_concrete<2048>>());
120 tv.push_back(std::make_unique<test17_concrete<4096>>());
121 tv.push_back(std::make_unique<test17_concrete<4096*16>>());
122 tv.push_back(std::make_unique<test17_concrete<4096*256>>());
123
124 std::default_random_engine random_engine(testing::local_random_engine());
125 std::uniform_int_distribution<> type_dist(0, 1);
126 std::uniform_int_distribution<size_t> size_dist(0, tv.size() - 1);
127 std::uniform_real_distribution<> which_dist(0, 1);
128
129 std::vector<test17::handle> allocs;
130 for (unsigned i = 0; i < 10000; ++i) {
131 auto type = type_dist(random_engine);
132 switch (type) {
133 case 0: {
134 size_t sz_idx = size_dist(random_engine);
135 allocs.push_back(tv[sz_idx]->alloc());
136 break;
137 }
138 case 1:
139 if (!allocs.empty()) {
140 size_t idx = which_dist(random_engine) * allocs.size();
141 std::swap(allocs[idx], allocs.back());
142 allocs.pop_back();
143 }
144 break;
145 }
146 }
147 }
148
149 #else
150
151 void test_cpp17_aligned_allocator() {
152 }
153
154 #endif
155
156 int main(int ac, char** av) {
157 namespace bpo = boost::program_options;
158 bpo::options_description opts("Allowed options");
159 opts.add_options()
160 ("help", "produce this help message")
161 ("iterations", bpo::value<unsigned>(), "run s specified number of iterations")
162 ("time", bpo::value<float>()->default_value(5.0), "run for a specified amount of time, in seconds")
163 ("random-seed", boost::program_options::value<unsigned>(), "Random number generator seed");
164 ;
165 bpo::variables_map vm;
166 bpo::store(bpo::parse_command_line(ac, av, opts), vm);
167 bpo::notify(vm);
168 test_cpp17_aligned_allocator();
169 auto seed = vm.count("random-seed") ? vm["random-seed"].as<unsigned>() : std::random_device{}();
170 std::default_random_engine random_engine(seed);
171 std::exponential_distribution<> distr(0.2);
172 std::uniform_int_distribution<> type(0, 1);
173 std::uniform_int_distribution<char> poison(-128, 127);
174 std::uniform_real_distribution<> which(0, 1);
175 std::vector<allocation> allocations;
176 auto iteration = [&] {
177 auto typ = type(random_engine);
178 switch (typ) {
179 case 0: {
180 size_t n = std::min<double>(std::exp(distr(random_engine)), 1 << 25);
181 try {
182 allocations.emplace_back(n, poison(random_engine));
183 } catch (std::bad_alloc&) {
184
185 }
186 break;
187 }
188 case 1: {
189 if (allocations.empty()) {
190 break;
191 }
192 size_t i = which(random_engine) * allocations.size();
193 allocations[i] = std::move(allocations.back());
194 allocations.pop_back();
195 break;
196 }
197 }
198 };
199 if (vm.count("help")) {
200 std::cout << opts << "\n";
201 return 1;
202 }
203 std::cout << "random-seed=" << seed << "\n";
204 if (vm.count("iterations")) {
205 auto iterations = vm["iterations"].as<unsigned>();
206 for (unsigned i = 0; i < iterations; ++i) {
207 iteration();
208 }
209 } else {
210 auto time = vm["time"].as<float>();
211 using clock = steady_clock_type;
212 auto end = clock::now() + std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::seconds(1) * time);
213 while (clock::now() < end) {
214 for (unsigned i = 0; i < 1000; ++i) {
215 iteration();
216 }
217 }
218 }
219 return 0;
220 }