~ubuntu-branches/ubuntu/quantal/ceph/quantal

« back to all changes in this revision

Viewing changes to src/test/omap_bench.cc

  • Committer: Package Import Robot
  • Author(s): James Page
  • Date: 2012-07-16 09:56:24 UTC
  • mfrom: (0.3.11)
  • mto: This revision was merged to the branch mainline in revision 17.
  • Revision ID: package-import@ubuntu.com-20120716095624-azr2w4hbhei1rxmx
Tags: upstream-0.48
ImportĀ upstreamĀ versionĀ 0.48

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * Generate latency statistics for a configurable number of object map 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"
 
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"
 
23
 
 
24
#include <string>
 
25
#include <iostream>
 
26
#include <cassert>
 
27
#include <climits>
 
28
#include <cmath>
 
29
 
 
30
using namespace std;
 
31
using ceph::bufferlist;
 
32
 
 
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;
 
54
        }
 
55
        else if (strcmp("uniform", args[i+1]) == 0) {
 
56
          omap_generator = OmapBench::generate_uniform_omap;
 
57
        }
 
58
      } else if (strcmp(args[i], "--name") == 0) {
 
59
        rados_id = args[i+1];
 
60
      }
 
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"
 
66
           << "OPTIONS\n"
 
67
           << " -t              number of threads to use (default "<<threads;
 
68
      cout << ")\n"
 
69
           << " -o              number of objects to write (default "<<objects;
 
70
      cout << ")\n"
 
71
           << " --entries       number of entries per object map (default "
 
72
           << omap_entries;
 
73
      cout <<")\n"
 
74
           << " --keysize       number of characters per object map key "
 
75
           << "(default "<<omap_key_size;
 
76
      cout << ")\n"
 
77
           << " --valsize       number of characters per object map value "
 
78
           << "(default "<<omap_value_size;
 
79
      cout << ")\n"
 
80
           << " --inc           specify the increment to use in the displayed "
 
81
           << "histogram (default "<<increment;
 
82
      cout << ")\n"
 
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;
 
89
      cout<<")\n";
 
90
      exit(1);
 
91
    }
 
92
  }
 
93
  int r = rados.init(rados_id.c_str());
 
94
  if (r < 0) {
 
95
    cout << "error during init" << std::endl;
 
96
    return r;
 
97
  }
 
98
  r = rados.conf_parse_argv(argc, argv);
 
99
  if (r < 0) {
 
100
    cout << "error during parsing args" << std::endl;
 
101
    return r;
 
102
  }
 
103
  r = rados.conf_parse_env(NULL);
 
104
  if (r < 0) {
 
105
    cout << "error during parsing env" << std::endl;
 
106
    return r;
 
107
  }
 
108
  r = rados.conf_read_file(NULL);
 
109
  if (r < 0) {
 
110
    cout << "error during read file" << std::endl;
 
111
    return r;
 
112
  }
 
113
  r = rados.connect();
 
114
  if (r < 0) {
 
115
    cout << "error during connect" << std::endl;
 
116
    return r;
 
117
  }
 
118
  r = rados.ioctx_create(pool_name.c_str(), io_ctx);
 
119
  if (r < 0) {
 
120
    cout << "error creating io ctx" << std::endl;
 
121
    rados.shutdown();
 
122
    return r;
 
123
  }
 
124
  return 0;
 
125
}
 
126
 
 
127
//Writer functions
 
128
Writer::Writer(OmapBench *omap_bench) : ob(omap_bench) {
 
129
  stringstream name;
 
130
  ob->data_lock.Lock();
 
131
  name << omap_bench->prefix << ++(ob->data.started_ops);
 
132
  ob->data_lock.Unlock();
 
133
  oid = name.str();
 
134
}
 
135
void Writer::start_time() {
 
136
  begin_time = ceph_clock_now(g_ceph_context);
 
137
}
 
138
void Writer::stop_time() {
 
139
  end_time = ceph_clock_now(g_ceph_context);
 
140
}
 
141
double Writer::get_time() {
 
142
  return (end_time - begin_time) * 1000;
 
143
}
 
144
string Writer::get_oid() {
 
145
  return oid;
 
146
}
 
147
std::map<std::string, bufferlist> & Writer::get_omap() {
 
148
  return omap;
 
149
}
 
150
 
 
151
//AioWriter functions
 
152
AioWriter::AioWriter(OmapBench *ob) : Writer(ob) {
 
153
  aioc = NULL;
 
154
}
 
155
AioWriter::~AioWriter() {
 
156
  if(aioc) aioc->release();
 
157
}
 
158
librados::AioCompletion * AioWriter::get_aioc() {
 
159
  return aioc;
 
160
}
 
161
void AioWriter::set_aioc(librados::callback_t complete,
 
162
    librados::callback_t safe) {
 
163
  aioc = ob->rados.aio_create_completion(this, complete, safe);
 
164
}
 
165
 
 
166
void OmapBench::aio_is_safe(rados_completion_t c, void *arg) {
 
167
  AioWriter *aiow = reinterpret_cast<AioWriter *>(arg);
 
168
  aiow->stop_time();
 
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();
 
176
  if (err < 0) {
 
177
    cout << "error writing AioCompletion";
 
178
    return;
 
179
  }
 
180
  double time = aiow->get_time();
 
181
  delete aiow;
 
182
  data_lock->Lock();
 
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;
 
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
  }
 
197
  data_lock->Unlock();
 
198
 
 
199
  thread_is_free_lock->Lock();
 
200
  busythreads_count--;
 
201
  thread_is_free->Signal();
 
202
  thread_is_free_lock->Unlock();
 
203
}
 
204
 
 
205
string 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
 
 
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) {
 
220
  bufferlist bl;
 
221
  stringstream data;
 
222
  int err = 0;
 
223
 
 
224
  //setup omap
 
225
  for (int i = 0; i < omap_entries; i++) {
 
226
    bufferlist omap_val;
 
227
    omap_val.append(random_string(value_size));
 
228
    string key = random_string(key_size);
 
229
    (*out_omap)[key]= omap_val;
 
230
  }
 
231
  if (err < 0) {
 
232
    cout << "generating uniform omap failed - "
 
233
        << "appending random string to omap failed" << std::endl;
 
234
  }
 
235
  return err;
 
236
}
 
237
 
 
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) {
 
241
  bufferlist bl;
 
242
  stringstream data;
 
243
  int err = 0;
 
244
 
 
245
  int num_entries = rand() % omap_entries + 1;
 
246
  int key_len = rand() % key_size +1;
 
247
  int val_len = rand() % value_size +1;
 
248
 
 
249
  //setup omap
 
250
  for (int i = 0; i < num_entries; i++) {
 
251
    bufferlist omap_val;
 
252
    omap_val.append(random_string(val_len));
 
253
    string key = random_string(key_len);
 
254
    (*out_omap)[key] = omap_val;
 
255
  }
 
256
  if (err < 0) {
 
257
    cout << "generating non-uniform omap failed - "
 
258
        "appending random string to omap failed" << std::endl;
 
259
  }
 
260
  return err;
 
261
}
 
262
 
 
263
 
 
264
int OmapBench::write_omap_asynchronously(AioWriter *aiow,
 
265
    const std::map<std::string,bufferlist> &omap) {
 
266
  librados::ObjectWriteOperation owo;
 
267
  owo.create(false);
 
268
  owo.omap_clear();
 
269
  owo.omap_set(omap);
 
270
  aiow->start_time();
 
271
  int err = io_ctx.aio_operate(aiow->get_oid(), aiow->get_aioc(), &owo);
 
272
  if (err < 0) {
 
273
    cout << "writing omap failed with code "<<err;
 
274
    cout << std::endl;
 
275
    return err;
 
276
  }
 
277
  return 0;
 
278
}
 
279
 
 
280
int OmapBench::write_objects_in_parallel(omap_generator_t omap_gen) {
 
281
  comp = NULL;
 
282
  AioWriter *this_aio_writer;
 
283
 
 
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);
 
291
      if (err < 0) {
 
292
        return err;
 
293
      }
 
294
    }
 
295
 
 
296
    //set up the write
 
297
    this_aio_writer = new AioWriter(this);
 
298
    this_aio_writer->set_aioc(NULL,safe);
 
299
 
 
300
    //perform the write
 
301
    busythreads_count++;
 
302
    int err = omap_gen(omap_entries, omap_key_size, omap_value_size,
 
303
        & this_aio_writer->get_omap());
 
304
    if (err < 0) {
 
305
      return err;
 
306
    }
 
307
    err = OmapBench::write_omap_asynchronously(this_aio_writer,
 
308
        (this_aio_writer->get_omap()));
 
309
 
 
310
 
 
311
    if (err < 0) {
 
312
      return err;
 
313
    }
 
314
  }
 
315
  while(busythreads_count > 0) {
 
316
    thread_is_free.Wait(thread_is_free_lock);
 
317
  }
 
318
 
 
319
  return 0;
 
320
}
 
321
 
 
322
int OmapBench::run() {
 
323
  return (((OmapBench *)this)->*OmapBench::test)(omap_generator);
 
324
}
 
325
 
 
326
int OmapBench::print_written_omap() {
 
327
  for (int i = 1; i <= objects; i++) {
 
328
    int err = 0;
 
329
    librados::ObjectReadOperation key_read;
 
330
    set<string> out_keys;
 
331
    map<string, bufferlist> out_vals;
 
332
    std::stringstream objstrm;
 
333
    objstrm << prefix;
 
334
    objstrm << i;
 
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);
 
338
    if (err < 0) {
 
339
      cout << "error " << err;
 
340
      cout << " getting omap key set " << std::endl;
 
341
      return err;
 
342
    }
 
343
 
 
344
    librados::ObjectReadOperation val_read;
 
345
    val_read.omap_get_vals_by_keys(out_keys, &out_vals, &err);
 
346
    if (err < 0) {
 
347
      cout << "error " << err;
 
348
      cout << " getting omap value set " << std::endl;
 
349
      return err;
 
350
    }
 
351
    io_ctx.operate(objstrm.str(), &val_read, NULL);
 
352
 
 
353
    for (set<string>::iterator iter = out_keys.begin();
 
354
        iter != out_keys.end(); ++iter) {
 
355
      cout << *iter << "\t" << (out_vals)[*iter] << std::endl;
 
356
    }
 
357
  }
 
358
  return 0;
 
359
}
 
360
 
 
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;
 
368
  cout << std::endl;
 
369
  cout << std::endl;
 
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;
 
377
  cout << 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;
 
382
    cout << "ms";
 
383
    int spaces;
 
384
    if (i == 0) spaces = 4;
 
385
    else spaces = 3 - floor(log10(i));
 
386
    for (int j = 0; j < spaces; j++) {
 
387
      cout << " ";
 
388
    }
 
389
    cout << "[";
 
390
    for(int j = 0; j < ((data.freq_map)[i])*45/(data.mode.second); j++) {
 
391
      cout << "*";
 
392
    }
 
393
  }
 
394
  cout << "\n========================================================"
 
395
       << std::endl;
 
396
}
 
397
 
 
398
/**
 
399
 * runs the specified test with the specified parameters and generates
 
400
 * a histogram of latencies
 
401
 */
 
402
int main(int argc, const char** argv) {
 
403
  OmapBench ob;
 
404
  int err = ob.setup(argc, argv);
 
405
  if (err<0) {
 
406
    cout << "error during setup: "<<err;
 
407
    cout << std::endl;
 
408
    exit(1);
 
409
  }
 
410
  err = ob.run();
 
411
  if (err < 0) {
 
412
    cout << "writing objects failed with code " << err;
 
413
    cout << std::endl;
 
414
    return err;
 
415
  }
 
416
 
 
417
  ob.print_results();
 
418
 
 
419
  //uncomment to show omaps
 
420
  /*err = ob.return print_written_omap();
 
421
  if (err < 0) {
 
422
    cout << "printing omaps failed with code " << err;
 
423
    cout << std::endl;
 
424
    return err;
 
425
  }
 
426
  */
 
427
  return 0;
 
428
 
 
429
}