2
* Generate latency statistics for a configurable number of object map write
3
* operations of configurable size.
5
* Created on: May 21, 2012
6
* Author: Eleanor Cawthon
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.
14
#include "include/rados/librados.hpp"
15
#include "include/Context.h"
16
#include "common/ceph_context.h"
17
#include "common/Mutex.h"
18
#include "common/Cond.h"
19
#include "include/utime.h"
20
#include "global/global_context.h"
21
#include "common/ceph_argparse.h"
22
#include "omap_bench.hpp"
31
using ceph::bufferlist;
33
int OmapBench::setup(int argc, const char** argv) {
34
//parse omap_bench args
35
vector<const char*> args;
36
argv_to_vec(argc,argv,args);
37
for (unsigned i = 0; i < args.size(); i++) {
38
if(i < args.size() - 1) {
39
if (strcmp(args[i], "-t") == 0) {
40
threads = atoi(args[i+1]);
41
} else if (strcmp(args[i], "-o") == 0) {
42
objects = atoi(args[i+1]);
43
} else if (strcmp(args[i], "--entries") == 0) {
44
omap_entries = atoi(args[i+1]);
45
} else if (strcmp(args[i], "--keysize") == 0) {
46
omap_key_size = atoi(args[i+1]);
47
} else if (strcmp(args[i], "--valsize") == 0) {
48
omap_value_size = atoi(args[i+1]);
49
} else if (strcmp(args[i], "--inc") == 0) {
50
increment = atoi(args[i+1]);
51
} else if (strcmp(args[i], "--omaptype") == 0) {
52
if(strcmp("rand",args[i+1]) == 0) {
53
omap_generator = OmapBench::generate_non_uniform_omap;
55
else if (strcmp("uniform", args[i+1]) == 0) {
56
omap_generator = OmapBench::generate_uniform_omap;
58
} else if (strcmp(args[i], "--name") == 0) {
61
} else if (strcmp(args[i], "--help") == 0) {
62
cout << "\nUsage: omapbench [options]\n"
63
<< "Generate latency statistics for a configurable number of "
64
<< "object map write operations of\n"
65
<< "configurable size.\n\n"
67
<< " -t number of threads to use (default "<<threads;
69
<< " -o number of objects to write (default "<<objects;
71
<< " --entries number of entries per object map (default "
74
<< " --keysize number of characters per object map key "
75
<< "(default "<<omap_key_size;
77
<< " --valsize number of characters per object map value "
78
<< "(default "<<omap_value_size;
80
<< " --inc specify the increment to use in the displayed "
81
<< "histogram (default "<<increment;
83
<< " --omaptype specify how omaps should be generated - "
84
<< "rand for random sizes between\n"
85
<< " 0 and max size, uniform for all sizes"
86
<< " to be specified size.\n"
87
<< " (default "<<omap_value_size;
88
cout <<"\n --name the rados id to use (default "<<rados_id;
93
int r = rados.init(rados_id.c_str());
95
cout << "error during init" << std::endl;
98
r = rados.conf_parse_argv(argc, argv);
100
cout << "error during parsing args" << std::endl;
103
r = rados.conf_parse_env(NULL);
105
cout << "error during parsing env" << std::endl;
108
r = rados.conf_read_file(NULL);
110
cout << "error during read file" << std::endl;
115
cout << "error during connect" << std::endl;
118
r = rados.ioctx_create(pool_name.c_str(), io_ctx);
120
cout << "error creating io ctx" << std::endl;
128
Writer::Writer(OmapBench *omap_bench) : ob(omap_bench) {
130
ob->data_lock.Lock();
131
name << omap_bench->prefix << ++(ob->data.started_ops);
132
ob->data_lock.Unlock();
135
void Writer::start_time() {
136
begin_time = ceph_clock_now(g_ceph_context);
138
void Writer::stop_time() {
139
end_time = ceph_clock_now(g_ceph_context);
141
double Writer::get_time() {
142
return (end_time - begin_time) * 1000;
144
string Writer::get_oid() {
147
std::map<std::string, bufferlist> & Writer::get_omap() {
151
//AioWriter functions
152
AioWriter::AioWriter(OmapBench *ob) : Writer(ob) {
155
AioWriter::~AioWriter() {
156
if(aioc) aioc->release();
158
librados::AioCompletion * AioWriter::get_aioc() {
161
void AioWriter::set_aioc(librados::callback_t complete,
162
librados::callback_t safe) {
163
aioc = ob->rados.aio_create_completion(this, complete, safe);
166
void OmapBench::aio_is_safe(rados_completion_t c, void *arg) {
167
AioWriter *aiow = reinterpret_cast<AioWriter *>(arg);
169
Mutex * data_lock = &aiow->ob->data_lock;
170
Mutex * thread_is_free_lock = &aiow->ob->thread_is_free_lock;
171
Cond * thread_is_free = &aiow->ob->thread_is_free;
172
int &busythreads_count = aiow->ob->busythreads_count;
173
omap_bench_data &data = aiow->ob->data;
174
int INCREMENT = aiow->ob->increment;
175
int err = aiow->get_aioc()->get_return_value();
177
cout << "error writing AioCompletion";
180
double time = aiow->get_time();
183
data.avg_latency = (data.avg_latency * data.completed_ops + time) / (data.completed_ops + 1);
184
data.completed_ops++;
185
if (time < data.min_latency) {
186
data.min_latency = time;
188
if (time > data.max_latency) {
189
data.max_latency = time;
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];
199
thread_is_free_lock->Lock();
201
thread_is_free->Signal();
202
thread_is_free_lock->Unlock();
205
string OmapBench::random_string(int len) {
207
string alphanum = "0123456789"
208
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
209
"abcdefghijklmnopqrstuvwxyz";
211
for (int i = 0; i < len; ++i) {
212
ret.push_back(alphanum[rand() % (alphanum.size()) - 1]);
218
int OmapBench::generate_uniform_omap(const int omap_entries, const int key_size,
219
const int value_size, std::map<std::string,bufferlist> * out_omap) {
225
for (int i = 0; i < omap_entries; i++) {
227
omap_val.append(random_string(value_size));
228
string key = random_string(key_size);
229
(*out_omap)[key]= omap_val;
232
cout << "generating uniform omap failed - "
233
<< "appending random string to omap failed" << std::endl;
238
int OmapBench::generate_non_uniform_omap(const int omap_entries,
239
const int key_size, const int value_size,
240
std::map<std::string,bufferlist> * out_omap) {
245
int num_entries = rand() % omap_entries + 1;
246
int key_len = rand() % key_size +1;
247
int val_len = rand() % value_size +1;
250
for (int i = 0; i < num_entries; i++) {
252
omap_val.append(random_string(val_len));
253
string key = random_string(key_len);
254
(*out_omap)[key] = omap_val;
257
cout << "generating non-uniform omap failed - "
258
"appending random string to omap failed" << std::endl;
264
int OmapBench::write_omap_asynchronously(AioWriter *aiow,
265
const std::map<std::string,bufferlist> &omap) {
266
librados::ObjectWriteOperation owo;
271
int err = io_ctx.aio_operate(aiow->get_oid(), aiow->get_aioc(), &owo);
273
cout << "writing omap failed with code "<<err;
280
int OmapBench::write_objects_in_parallel(omap_generator_t omap_gen) {
282
AioWriter *this_aio_writer;
284
Mutex::Locker l(thread_is_free_lock);
285
for (int i = 0; i < objects; i++) {
286
assert(busythreads_count <= threads);
287
//wait for a writer to be free
288
if (busythreads_count == threads) {
289
int err = thread_is_free.Wait(thread_is_free_lock);
290
assert(busythreads_count < threads);
297
this_aio_writer = new AioWriter(this);
298
this_aio_writer->set_aioc(NULL,safe);
302
int err = omap_gen(omap_entries, omap_key_size, omap_value_size,
303
& this_aio_writer->get_omap());
307
err = OmapBench::write_omap_asynchronously(this_aio_writer,
308
(this_aio_writer->get_omap()));
315
while(busythreads_count > 0) {
316
thread_is_free.Wait(thread_is_free_lock);
322
int OmapBench::run() {
323
return (((OmapBench *)this)->*OmapBench::test)(omap_generator);
326
int OmapBench::print_written_omap() {
327
for (int i = 1; i <= objects; i++) {
329
librados::ObjectReadOperation key_read;
330
set<string> out_keys;
331
map<string, bufferlist> out_vals;
332
std::stringstream objstrm;
335
cout << "\nPrinting omap for "<<objstrm.str() << std::endl;
336
key_read.omap_get_keys("", LONG_MAX, &out_keys, &err);
337
io_ctx.operate(objstrm.str(), &key_read, NULL);
339
cout << "error " << err;
340
cout << " getting omap key set " << std::endl;
344
librados::ObjectReadOperation val_read;
345
val_read.omap_get_vals_by_keys(out_keys, &out_vals, &err);
347
cout << "error " << err;
348
cout << " getting omap value set " << std::endl;
351
io_ctx.operate(objstrm.str(), &val_read, NULL);
353
for (set<string>::iterator iter = out_keys.begin();
354
iter != out_keys.end(); ++iter) {
355
cout << *iter << "\t" << (out_vals)[*iter] << std::endl;
361
void OmapBench::print_results() {
362
cout << "========================================================";
363
cout << "\nNumber of object maps written:\t" << objects;
364
cout << "\nNumber of threads used:\t\t" << threads;
365
cout << "\nEntries per object map:\t\t" << omap_entries;
366
cout << "\nCharacters per object map key:\t" <<omap_key_size;
367
cout << "\nCharacters per object map val:\t" << omap_value_size;
370
cout << "Average latency:\t" << data.avg_latency;
371
cout << "ms\nMinimum latency:\t" << data.min_latency;
372
cout << "ms\nMaximum latency:\t" << data.max_latency;
373
cout << "ms\nMode latency:\t\t"<<"between "<<data.mode.first * increment;
374
cout << " and " <<data.mode.first * increment + increment;
375
cout << "ms\nTotal latency:\t\t" << data.total_latency;
376
cout << "ms"<<std::endl;
378
cout << "Histogram:" << std::endl;
379
for(int i = floor(data.min_latency / increment); i <
380
ceil(data.max_latency / increment); i++) {
381
cout << ">= "<< i * increment;
384
if (i == 0) spaces = 4;
385
else spaces = 3 - floor(log10(i));
386
for (int j = 0; j < spaces; j++) {
390
for(int j = 0; j < ((data.freq_map)[i])*45/(data.mode.second); j++) {
394
cout << "\n========================================================"
399
* runs the specified test with the specified parameters and generates
400
* a histogram of latencies
402
int main(int argc, const char** argv) {
404
int err = ob.setup(argc, argv);
406
cout << "error during setup: "<<err;
412
cout << "writing objects failed with code " << err;
419
//uncomment to show omaps
420
/*err = ob.return print_written_omap();
422
cout << "printing omaps failed with code " << err;