]> git.proxmox.com Git - ceph.git/blame - ceph/src/test/omap_bench.cc
import ceph quincy 17.2.6
[ceph.git] / ceph / src / test / omap_bench.cc
CommitLineData
7c673cae
FG
1/*
2 * Generate latency statistics for a configurable number of write
3 * operations of configurable size.
4 *
5 * Created on: May 21, 2012
6 * Author: Eleanor Cawthon
7 *
8 * This is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License version 2.1, as published by the Free Software
11 * Foundation. See file COPYING.
12 */
13
14#include "include/rados/librados.hpp"
15#include "include/Context.h"
16#include "common/ceph_context.h"
9f95a23c 17#include "common/ceph_mutex.h"
7c673cae
FG
18#include "common/Cond.h"
19#include "include/utime.h"
7c673cae
FG
20#include "common/ceph_argparse.h"
21#include "test/omap_bench.h"
22
23#include <string>
24#include <iostream>
25#include <cassert>
26#include <climits>
27#include <cmath>
28
29using namespace std;
30using ceph::bufferlist;
31
32int OmapBench::setup(int argc, const char** argv) {
33 //parse key_value_store_bench args
20effc67 34 auto args = argv_to_vec(argc, argv);
7c673cae
FG
35 for (unsigned i = 0; i < args.size(); i++) {
36 if(i < args.size() - 1) {
37 if (strcmp(args[i], "-t") == 0) {
38 threads = atoi(args[i+1]);
39 } else if (strcmp(args[i], "-o") == 0) {
40 objects = atoi(args[i+1]);
41 } else if (strcmp(args[i], "--entries") == 0) {
42 entries_per_omap = atoi(args[i+1]);
43 } else if (strcmp(args[i], "--keysize") == 0) {
44 key_size = atoi(args[i+1]);
45 } else if (strcmp(args[i], "--valsize") == 0) {
46 value_size = atoi(args[i+1]);
47 } else if (strcmp(args[i], "--inc") == 0) {
48 increment = atoi(args[i+1]);
49 } else if (strcmp(args[i], "--omaptype") == 0) {
50 if(strcmp("rand",args[i+1]) == 0) {
51 omap_generator = OmapBench::generate_non_uniform_omap;
52 }
53 else if (strcmp("uniform", args[i+1]) == 0) {
54 omap_generator = OmapBench::generate_uniform_omap;
55 }
56 } else if (strcmp(args[i], "--name") == 0) {
57 rados_id = args[i+1];
58 }
59 } else if (strcmp(args[i], "--help") == 0) {
60 cout << "\nUsage: ostorebench [options]\n"
61 << "Generate latency statistics for a configurable number of "
62 << "key value pair operations of\n"
63 << "configurable size.\n\n"
64 << "OPTIONS\n"
65 << " -t number of threads to use (default "<<threads;
66 cout << ")\n"
67 << " -o number of objects to write (default "<<objects;
68 cout << ")\n"
69 << " --entries number of entries per (default "
70 << entries_per_omap;
71 cout <<")\n"
72 << " --keysize number of characters per key "
73 << "(default "<<key_size;
74 cout << ")\n"
75 << " --valsize number of characters per value "
76 << "(default "<<value_size;
77 cout << ")\n"
78 << " --inc specify the increment to use in the displayed "
79 << "histogram (default "<<increment;
80 cout << ")\n"
81 << " --omaptype specify how omaps should be generated - "
82 << "rand for random sizes between\n"
83 << " 0 and max size, uniform for all sizes"
84 << " to be specified size.\n"
85 << " (default uniform)\n";
86 cout << " --name the rados id to use (default "<< rados_id
87 << ")\n";
88 exit(1);
89 }
90 }
91 int r = rados.init(rados_id.c_str());
92 if (r < 0) {
93 cout << "error during init" << std::endl;
94 return r;
95 }
96 r = rados.conf_parse_argv(argc, argv);
97 if (r < 0) {
98 cout << "error during parsing args" << std::endl;
99 return r;
100 }
101 r = rados.conf_parse_env(NULL);
102 if (r < 0) {
103 cout << "error during parsing env" << std::endl;
104 return r;
105 }
106 r = rados.conf_read_file(NULL);
107 if (r < 0) {
108 cout << "error during read file" << std::endl;
109 return r;
110 }
111 r = rados.connect();
112 if (r < 0) {
113 cout << "error during connect" << std::endl;
114 return r;
115 }
116 r = rados.ioctx_create(pool_name.c_str(), io_ctx);
117 if (r < 0) {
118 cout << "error creating io ctx" << std::endl;
119 rados.shutdown();
120 return r;
121 }
122 return 0;
123}
124
125//Writer functions
126Writer::Writer(OmapBench *omap_bench) : ob(omap_bench) {
127 stringstream name;
9f95a23c 128 ob->data_lock.lock();
7c673cae 129 name << omap_bench->prefix << ++(ob->data.started_ops);
9f95a23c 130 ob->data_lock.unlock();
7c673cae
FG
131 oid = name.str();
132}
133void Writer::start_time() {
134 begin_time = ceph_clock_now();
135}
136void Writer::stop_time() {
137 end_time = ceph_clock_now();
138}
139double Writer::get_time() {
140 return (end_time - begin_time) * 1000;
141}
142string Writer::get_oid() {
143 return oid;
144}
145std::map<std::string, bufferlist> & Writer::get_omap() {
146 return omap;
147}
148
149//AioWriter functions
150AioWriter::AioWriter(OmapBench *ob) : Writer(ob) {
151 aioc = NULL;
152}
153AioWriter::~AioWriter() {
154 if(aioc) aioc->release();
155}
156librados::AioCompletion * AioWriter::get_aioc() {
157 return aioc;
158}
9f95a23c
TL
159void AioWriter::set_aioc(librados::callback_t complete) {
160 aioc = ob->rados.aio_create_completion(this, complete);
7c673cae
FG
161}
162
163
164//Helper methods
9f95a23c 165void OmapBench::aio_is_complete(rados_completion_t c, void *arg) {
7c673cae
FG
166 AioWriter *aiow = reinterpret_cast<AioWriter *>(arg);
167 aiow->stop_time();
9f95a23c
TL
168 ceph::mutex * data_lock = &aiow->ob->data_lock;
169 ceph::mutex * thread_is_free_lock = &aiow->ob->thread_is_free_lock;
170 ceph::condition_variable* thread_is_free = &aiow->ob->thread_is_free;
7c673cae
FG
171 int &busythreads_count = aiow->ob->busythreads_count;
172 o_bench_data &data = aiow->ob->data;
173 int INCREMENT = aiow->ob->increment;
174 int err = aiow->get_aioc()->get_return_value();
175 if (err < 0) {
176 cout << "error writing AioCompletion";
177 return;
178 }
179 double time = aiow->get_time();
180 delete aiow;
9f95a23c 181 data_lock->lock();
7c673cae
FG
182 data.avg_latency = (data.avg_latency * data.completed_ops + time)
183 / (data.completed_ops + 1);
184 data.completed_ops++;
185 if (time < data.min_latency) {
186 data.min_latency = time;
187 }
188 if (time > data.max_latency) {
189 data.max_latency = time;
190 }
191 data.total_latency += time;
192 ++(data.freq_map[time / INCREMENT]);
193 if(data.freq_map[time/INCREMENT] > data.mode.second) {
194 data.mode.first = time/INCREMENT;
195 data.mode.second = data.freq_map[time/INCREMENT];
196 }
9f95a23c 197 data_lock->unlock();
7c673cae 198
9f95a23c 199 thread_is_free_lock->lock();
7c673cae 200 busythreads_count--;
9f95a23c
TL
201 thread_is_free->notify_all();
202 thread_is_free_lock->unlock();
7c673cae
FG
203}
204
205string OmapBench::random_string(int len) {
206 string ret;
207 string alphanum = "0123456789"
208 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
209 "abcdefghijklmnopqrstuvwxyz";
210
211 for (int i = 0; i < len; ++i) {
212 ret.push_back(alphanum[rand() % (alphanum.size() - 1)]);
213 }
214
215 return ret;
216}
217
218int OmapBench::run() {
219 return (((OmapBench *)this)->*OmapBench::test)(omap_generator);
220}
221
222int OmapBench::print_written_omap() {
223 for (int i = 1; i <= objects; i++) {
224 int err = 0;
225 librados::ObjectReadOperation key_read;
226 set<string> out_keys;
227 map<string, bufferlist> out_vals;
228 std::stringstream objstrm;
229 objstrm << prefix;
230 objstrm << i;
231 cout << "\nPrinting omap for "<<objstrm.str() << std::endl;
232 // FIXME: we ignore pmore here. this shouldn't happen for benchmark
233 // keys, though, unless the OSD limit is *really* low.
234 key_read.omap_get_keys2("", LONG_MAX, &out_keys, nullptr, &err);
235 io_ctx.operate(objstrm.str(), &key_read, NULL);
236 if (err < 0) {
237 cout << "error " << err;
238 cout << " getting omap key set " << std::endl;
239 return err;
240 }
241
242 librados::ObjectReadOperation val_read;
243 val_read.omap_get_vals_by_keys(out_keys, &out_vals, &err);
244 if (err < 0) {
245 cout << "error " << err;
246 cout << " getting omap value set " << std::endl;
247 return err;
248 }
249 io_ctx.operate(objstrm.str(), &val_read, NULL);
250
251 for (set<string>::iterator iter = out_keys.begin();
252 iter != out_keys.end(); ++iter) {
253 cout << *iter << "\t" << (out_vals)[*iter] << std::endl;
254 }
255 }
256 return 0;
257}
258
259void OmapBench::print_results() {
260 cout << "========================================================";
261 cout << "\nNumber of kvmaps written:\t" << objects;
262 cout << "\nNumber of ops at once:\t" << threads;
263 cout << "\nEntries per kvmap:\t\t" << entries_per_omap;
264 cout << "\nCharacters per key:\t" << key_size;
265 cout << "\nCharacters per val:\t" << value_size;
266 cout << std::endl;
267 cout << std::endl;
268 cout << "Average latency:\t" << data.avg_latency;
269 cout << "ms\nMinimum latency:\t" << data.min_latency;
270 cout << "ms\nMaximum latency:\t" << data.max_latency;
271 cout << "ms\nMode latency:\t\t"<<"between "<<data.mode.first * increment;
272 cout << " and " <<data.mode.first * increment + increment;
273 cout << "ms\nTotal latency:\t\t" << data.total_latency;
274 cout << "ms"<<std::endl;
275 cout << std::endl;
276 cout << "Histogram:" << std::endl;
277 for(int i = floor(data.min_latency / increment); i <
278 ceil(data.max_latency / increment); i++) {
279 cout << ">= "<< i * increment;
280 cout << "ms";
281 int spaces;
282 if (i == 0) spaces = 4;
283 else spaces = 3 - floor(log10(i));
284 for (int j = 0; j < spaces; j++) {
285 cout << " ";
286 }
287 cout << "[";
288 for(int j = 0; j < ((data.freq_map)[i])*45/(data.mode.second); j++) {
289 cout << "*";
290 }
291 cout << std::endl;
292 }
293 cout << "\n========================================================"
294 << std::endl;
295}
296
297int OmapBench::write_omap_asynchronously(AioWriter *aiow,
298 const std::map<std::string,bufferlist> &omap) {
299 librados::ObjectWriteOperation owo;
300 owo.create(false);
301 owo.omap_clear();
302 owo.omap_set(omap);
303 aiow->start_time();
304 int err = io_ctx.aio_operate(aiow->get_oid(), aiow->get_aioc(), &owo);
305 if (err < 0) {
306 cout << "writing omap failed with code "<<err;
307 cout << std::endl;
308 return err;
309 }
310 return 0;
311}
312
313//Omap Generators
314int OmapBench::generate_uniform_omap(const int omap_entries, const int key_size,
315 const int value_size, std::map<std::string,bufferlist> * out_omap) {
316 bufferlist bl;
317
318 //setup omap
319 for (int i = 0; i < omap_entries; i++) {
320 bufferlist omap_val;
321 omap_val.append(random_string(value_size));
322 string key = random_string(key_size);
323 (*out_omap)[key]= omap_val;
324 }
325 return 0;
326}
327
328int OmapBench::generate_non_uniform_omap(const int omap_entries,
329 const int key_size, const int value_size,
330 std::map<std::string,bufferlist> * out_omap) {
331 bufferlist bl;
332
333 int num_entries = rand() % omap_entries + 1;
334 int key_len = rand() % key_size +1;
335 int val_len = rand() % value_size +1;
336
337 //setup omap
338 for (int i = 0; i < num_entries; i++) {
339 bufferlist omap_val;
340 omap_val.append(random_string(val_len));
341 string key = random_string(key_len);
342 (*out_omap)[key] = omap_val;
343 }
344 return 0;
345}
346
347int OmapBench::generate_small_non_random_omap(const int omap_entries,
348 const int key_size, const int value_size,
349 std::map<std::string,bufferlist> * out_omap) {
350 bufferlist bl;
351 stringstream key;
352
353 //setup omap
354 for (int i = 0; i < omap_entries; i++) {
355 bufferlist omap_val;
356 omap_val.append("Value ");
357 omap_val.append(i);
358 key << "Key " << i;
359 (*out_omap)[key.str()]= omap_val;
360 }
361 return 0;
362}
363
364//tests
365int OmapBench::test_write_objects_in_parallel(omap_generator_t omap_gen) {
7c673cae
FG
366 AioWriter *this_aio_writer;
367
9f95a23c 368 std::unique_lock l{thread_is_free_lock};
7c673cae 369 for (int i = 0; i < objects; i++) {
11fdf7f2 370 ceph_assert(busythreads_count <= threads);
7c673cae
FG
371 //wait for a writer to be free
372 if (busythreads_count == threads) {
9f95a23c 373 thread_is_free.wait(l);
11fdf7f2 374 ceph_assert(busythreads_count < threads);
7c673cae
FG
375 }
376
377 //set up the write
378 this_aio_writer = new AioWriter(this);
9f95a23c 379 this_aio_writer->set_aioc(comp);
7c673cae
FG
380
381 //perform the write
382 busythreads_count++;
383 int err = omap_gen(entries_per_omap, key_size, value_size,
384 & this_aio_writer->get_omap());
385 if (err < 0) {
386 return err;
387 }
388 err = OmapBench::write_omap_asynchronously(this_aio_writer,
389 (this_aio_writer->get_omap()));
390
391
392 if (err < 0) {
393 return err;
394 }
395 }
9f95a23c 396 thread_is_free.wait(l, [this] { return busythreads_count <= 0;});
7c673cae
FG
397 return 0;
398}
399
400/**
401 * runs the specified test with the specified parameters and generates
402 * a histogram of latencies
403 */
404int main(int argc, const char** argv) {
405 OmapBench ob;
406 int err = ob.setup(argc, argv);
407 if (err<0) {
408 cout << "error during setup: "<<err;
409 cout << std::endl;
410 exit(1);
411 }
412 err = ob.run();
413 if (err < 0) {
414 cout << "writing objects failed with code " << err;
415 cout << std::endl;
416 return err;
417 }
418
419 ob.print_results();
420
421 //uncomment to show omaps
422 /*err = ob.return print_written_omap();
423 if (err < 0) {
424 cout << "printing omaps failed with code " << err;
425 cout << std::endl;
426 return err;
427 }
428 */
429 return 0;
430
431}