summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore1
-rw-r--r--src/Makefile.am4
-rw-r--r--src/test/omap_bench.cc413
-rw-r--r--src/test/omap_bench.hpp192
4 files changed, 610 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
index 6e045696fcc..595364b60cb 100644
--- a/.gitignore
+++ b/.gitignore
@@ -59,6 +59,7 @@ core
*.generated.dot
src/ocf/ceph
src/ocf/rbd
+src/omapbench
# temporary directory used by e.g. "make distcheck", e.g. ceph-0.42
/ceph-[0-9]*/
diff --git a/src/Makefile.am b/src/Makefile.am
index 9ac4f2f406e..ccddb7f88f1 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -214,6 +214,10 @@ testrados_SOURCES = test/osd/TestRados.cc test/osd/TestOpStat.cc test/osd/Object
testrados_LDADD = librados.la $(LIBGLOBAL_LDA)
bin_DEBUGPROGRAMS += testrados
+omapbench_SOURCES = test/omap_bench.cc
+omapbench_LDADD = librados.la $(LIBGLOBAL_LDA)
+bin_DEBUGPROGRAMS += omapbench
+
multi_stress_watch_SOURCES = test/multi_stress_watch.cc test/rados-api/test.cc
multi_stress_watch_LDADD = librados.la $(LIBGLOBAL_LDA)
bin_DEBUGPROGRAMS += multi_stress_watch
diff --git a/src/test/omap_bench.cc b/src/test/omap_bench.cc
new file mode 100644
index 00000000000..0bc7d860927
--- /dev/null
+++ b/src/test/omap_bench.cc
@@ -0,0 +1,413 @@
+/*
+ * Generate latency statistics for a configurable number of object map write
+ * operations of configurable size.
+ *
+ * Created on: May 21, 2012
+ * Author: Eleanor Cawthon
+ *
+ * This is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2.1, as published by the Free Software
+ * Foundation. See file COPYING.
+ */
+
+#include "include/rados/librados.hpp"
+#include "include/Context.h"
+#include "common/ceph_context.h"
+#include "common/Mutex.h"
+#include "common/Cond.h"
+#include "include/utime.h"
+#include "global/global_context.h"
+#include "common/ceph_argparse.h"
+#include "omap_bench.hpp"
+
+#include<string>
+#include<iostream>
+#include<cassert>
+#include<climits>
+
+using namespace std;
+using ceph::bufferlist;
+
+int OmapBench::setup(int argc, const char** argv) {
+ int r = rados.init(rados_id.c_str());
+ if (r < 0) {
+ return r;
+ }
+ r = rados.conf_read_file(NULL);
+ if (r < 0)
+ return r;
+ r = rados.conf_parse_argv(argc, argv);
+ if (r < 0)
+ return r;
+ r = rados.conf_parse_env(NULL);
+ if (r < 0)
+ return r;
+ r = rados.connect();
+ if (r < 0)
+ return r;
+ r = rados.ioctx_create(pool_name.c_str(), io_ctx);
+ if (r < 0) {
+ rados.shutdown();
+ return r;
+ }
+
+
+ //parse omap_bench args
+ vector<const char*> args;
+ argv_to_vec(argc,argv,args);
+ for (int i = 0;i < args.size(); i++) {
+ if (strcmp(args[i], "-t") == 0){
+ threads = atoi(args[i+1]);
+ } else if (strcmp(args[i],"-o") == 0){
+ objects = atoi(args[i+1]);
+ } else if (strcmp(args[i],"--entries") == 0){
+ omap_entries = atoi(args[i+1]);
+ } else if (strcmp(args[i],"--keysize") == 0){
+ omap_key_size = atoi(args[i+1]);
+ } else if (strcmp(args[i],"--valsize") == 0){
+ omap_value_size = atoi(args[i+1]);
+ } else if (strcmp(args[i],"--inc") == 0){
+ increment = atoi(args[i+1]);
+ } else if (strcmp(args[i],"--omaptype") == 0){
+ if(strcmp("rand",args[i+1]) == 0){
+ omap_generator = OmapBench::generate_non_uniform_omap;
+ }
+ else if (strcmp("uniform",args[i+1]) == 0){
+ omap_generator = OmapBench::generate_uniform_omap;
+ }
+ } else if (strcmp(args[i],"--help") == 0){
+ cout<< "\nUsage: omapbench [options]\n"
+ << "Generate latency statistics for a configurable number of "
+ << "object map write operations of\n"
+ << "configurable size.\n\n"
+ << "OPTIONS\n"
+ << " -t number of threads to use (default "<<threads;
+ cout<<")\n"
+ << " -o number of objects to write (default "<<objects;
+ cout<< ")\n"
+ << " --entries number of entries per object map (default "
+ <<omap_entries;
+ cout<<")\n"
+ << " --keysize number of characters per object map key "
+ << "(default "<<omap_key_size;
+ cout<<")\n"
+ << " --valsize number of characters per object map value "
+ << "(default "<<omap_value_size;
+ cout<<")\n"
+ << " --inc specify the increment to use in the displayed "
+ << "histogram (default "<<increment;
+ cout<<")\n"
+ << " --omaptype specify how omaps should be generated - "
+ << "rand for random sizes between\n"
+ << " 0 and the max size, uniform for all sizes"
+ << " to be specified size.\n"
+ << " (default "<<omap_value_size;
+ cout<<")\n";
+ exit(1);
+ }
+ }
+
+ return 0;
+}
+
+//Writer functions
+Writer::Writer(OmapBench *omap_bench) : ob(omap_bench){
+ stringstream name;
+ ob->data_lock.Lock();
+ name << "object number " << ++(ob->data.started_ops);
+ ob->data_lock.Unlock();
+ oid = name.str();
+}
+void Writer::start_time(){
+ begin_time = ceph_clock_now(g_ceph_context);
+}
+void Writer::stop_time(){
+ end_time = ceph_clock_now(g_ceph_context);
+}
+double Writer::get_time(){
+ return (end_time - begin_time) * 1000;
+}
+string Writer::get_oid(){
+ return oid;
+}
+std::map<std::string, bufferlist> & Writer::get_omap(){
+ return omap;
+}
+
+//AioWriter functions
+AioWriter::AioWriter(OmapBench *ob) : Writer(ob){
+ aioc = NULL;
+}
+AioWriter::~AioWriter(){
+ if(aioc) aioc->release();
+}
+librados::AioCompletion * AioWriter::get_aioc(){
+ return aioc;
+}
+void AioWriter::set_aioc(librados::callback_t complete,
+ librados::callback_t safe){
+ aioc = ob->rados.aio_create_completion(this, complete, safe);
+}
+
+void OmapBench::aio_is_safe(rados_completion_t c, void *arg) {
+ AioWriter *aiow = reinterpret_cast<AioWriter *>(arg);
+ aiow->stop_time();
+ Mutex * data_lock = &aiow->ob->data_lock;
+ Mutex * thread_is_free_lock = &aiow->ob->thread_is_free_lock;
+ Cond * thread_is_free = &aiow->ob->thread_is_free;
+ int &busythreads_count = aiow->ob->busythreads_count;
+ omap_bench_data &data = aiow->ob->data;
+ int INCREMENT = aiow->ob->increment;
+ int err = aiow->get_aioc()->get_return_value();
+ if (err < 0) {
+ cout << "error writing AioCompletion";
+ return;
+ }
+ double time = aiow->get_time();
+ delete aiow;
+ data_lock->Lock();
+ data.avg_latency = (data.avg_latency *
+ data.completed_ops + time) / ++(data.completed_ops);
+ if (time < data.min_latency){
+ data.min_latency = time;
+ }
+ if (time > data.max_latency){
+ data.max_latency = time;
+ }
+ data.total_latency += time;
+ ++(data.freq_map[time / INCREMENT]);
+ if(data.freq_map[time/INCREMENT] > data.mode.second){
+ data.mode.first = time/INCREMENT;
+ data.mode.second = data.freq_map[time/INCREMENT];
+ }
+ data_lock->Unlock();
+
+ thread_is_free_lock->Lock();
+ busythreads_count--;
+ thread_is_free->Signal();
+ thread_is_free_lock->Unlock();
+}
+
+string OmapBench::random_string(int len) {
+ string ret;
+ string alphanum = "0123456789"
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+ "abcdefghijklmnopqrstuvwxyz";
+
+ for (int i = 0; i < len; ++i) {
+ ret.push_back(alphanum[rand() % (alphanum.size()) - 1]);
+ }
+
+ return ret;
+}
+
+int OmapBench::generate_uniform_omap(const int omap_entries, const int key_size,
+ const int value_size, std::map<std::string,bufferlist> * out_omap){
+ bufferlist bl;
+ stringstream data;
+ int err = 0;
+
+ //setup omap
+ for (int i = 0; i < omap_entries; i++) {
+ bufferlist omap_val;
+ omap_val.append(random_string(value_size));
+ string key = random_string(key_size);
+ (*out_omap)[key]= omap_val;
+ }
+ if (err < 0){
+ cout << "generating uniform omap failed - "
+ << "appending random string to omap failed" << std::endl;
+ }
+ return err;
+}
+
+int OmapBench::generate_non_uniform_omap(const int omap_entries,
+ const int key_size, const int value_size,
+ std::map<std::string,bufferlist> * out_omap){
+ bufferlist bl;
+ stringstream data;
+ int err = 0;
+
+ int num_entries = rand() % omap_entries + 1;
+ int key_len = rand() % key_size +1;
+ int val_len = rand() % value_size +1;
+
+ //setup omap
+ for (int i = 0; i < num_entries; i++) {
+ bufferlist omap_val;
+ omap_val.append(random_string(val_len));
+ string key = random_string(key_len);
+ (*out_omap)[key] = omap_val;
+ }
+ if (err < 0){
+ cout << "generating non-uniform omap failed - "
+ "appending random string to omap failed" << std::endl;
+ }
+ return err;
+}
+
+
+int OmapBench::write_omap_asynchronously(AioWriter *aiow,
+ const std::map<std::string,bufferlist> &omap) {
+ librados::ObjectWriteOperation owo;
+ owo.create(false);
+ owo.omap_clear();
+ owo.omap_set(omap);
+ aiow->start_time();
+ int err = io_ctx.aio_operate(aiow->get_oid(), aiow->get_aioc(), &owo);
+ if (err < 0){
+ cout << "writing omap failed with code "<<err;
+ cout << std::endl;
+ return err;
+ }
+ return 0;
+}
+
+int OmapBench::write_objects_in_parallel(omap_generator_t omap_gen) {
+ comp = NULL;
+ AioWriter *this_aio_writer;
+
+ Mutex::Locker l(thread_is_free_lock);
+ for (int i = 0; i < objects; i++) {
+ assert(busythreads_count <= threads);
+ //wait for a writer to be free
+ if (busythreads_count == threads) {
+ int err = thread_is_free.Wait(thread_is_free_lock);
+ assert(busythreads_count < threads);
+ if (err < 0) {
+ return err;
+ }
+ }
+
+ //set up the write
+ this_aio_writer = new AioWriter(this);
+ this_aio_writer->set_aioc(NULL,safe);
+
+ //perform the write
+ busythreads_count++;
+ int err = omap_gen(omap_entries, omap_key_size, omap_value_size,
+ & this_aio_writer->get_omap());
+ if (err < 0) {
+ return err;
+ }
+ err = OmapBench::write_omap_asynchronously(this_aio_writer,
+ (this_aio_writer->get_omap()));
+
+
+ if (err < 0) {
+ return err;
+ }
+ }
+ while(busythreads_count > 0){
+ thread_is_free.Wait(thread_is_free_lock);
+ }
+
+ return 0;
+}
+
+int OmapBench::run(){
+ return (((OmapBench *)this)->*OmapBench::test)(omap_generator);
+}
+
+int OmapBench::print_written_omap(){
+ for (int i = 1; i <= objects; i++) {
+ int err = 0;
+ librados::ObjectReadOperation key_read;
+ set<string> out_keys;
+ map<string, bufferlist> out_vals;
+ std::stringstream objstrm;
+ objstrm << "object number ";
+ objstrm << i;
+ cout << "\nPrinting omap for "<<objstrm.str() << std::endl;
+ key_read.omap_get_keys("", LONG_MAX, &out_keys, &err);
+ io_ctx.operate(objstrm.str(), &key_read, NULL);
+ if (err < 0) {
+ cout << "error " << err;
+ cout << " getting omap key set " << std::endl;
+ return err;
+ }
+
+ librados::ObjectReadOperation val_read;
+ val_read.omap_get_vals_by_keys(out_keys, &out_vals, &err);
+ if (err < 0) {
+ cout << "error " << err;
+ cout << " getting omap value set " << std::endl;
+ return err;
+ }
+ io_ctx.operate(objstrm.str(), &val_read, NULL);
+
+ for (set<string>::iterator iter = out_keys.begin();
+ iter != out_keys.end(); ++iter) {
+ cout << *iter << "\t" << (out_vals)[*iter] << std::endl;
+ }
+ }
+ return 0;
+}
+
+void OmapBench::print_results(){
+ cout << "==============================================================="
+ << std::endl;
+ cout << "\nNumber of object maps written:\t" << objects;
+ cout << "\nNumber of threads used:\t\t" << threads;
+ cout << "\nEntries per object map:\t\t" << omap_entries;
+ cout << "\nCharacters per object map key:\t" <<omap_key_size;
+ cout << "\nCharacters per object map val:\t" << omap_value_size;
+ cout << std::endl;
+ cout << std::endl;
+ cout << "Average latency:\t" << data.avg_latency;
+ cout << "ms\nMinimum latency:\t" << data.min_latency;
+ cout << "ms\nMaximum latency:\t" << data.max_latency;
+ cout << "ms\nMode latency:\t\t"<<"between "<<data.mode.first * increment;
+ cout << " and " <<data.mode.first * increment + increment;
+ cout << "ms\nTotal latency:\t\t" << data.total_latency;
+ cout << "ms"<<std::endl;
+ cout << std::endl;
+ cout << "Histogram:" << std::endl;
+ for(int i = floor(data.min_latency / increment); i <
+ ceil(data.max_latency / increment); i++){
+ cout << ">= "<< i * increment;
+ cout << "ms";
+ if (i * increment < 100) cout << "\t";
+ cout << "\t[";
+ for(int j = 0; j < ((data.freq_map)[i])*45/(data.mode.second); j++){
+ cout << "*";
+ }
+ cout << std::endl;
+ }
+ cout << "\n==============================================================="
+ << std::endl;
+}
+
+/**
+ * runs the specified test with the specified parameters and generates
+ * a histogram of latencies
+ */
+int main(int argc, const char** argv) {
+ OmapBench ob;
+ int err = ob.setup(argc, argv);
+ if (err<0){
+ cout << "error during setup: "<<err;
+ cout << std::endl;
+ exit(1);
+ }
+ err = ob.run();
+ if (err < 0) {
+ cout << "writing objects failed with code " << err;
+ cout << std::endl;
+ return err;
+ }
+
+ ob.print_results();
+
+ //uncomment to show omaps
+ /*err = ob.return print_written_omap();
+ if (err < 0) {
+ cout << "printing omaps failed with code " << err;
+ cout << std::endl;
+ return err;
+ }
+ */
+ return 0;
+
+}
diff --git a/src/test/omap_bench.hpp b/src/test/omap_bench.hpp
new file mode 100644
index 00000000000..2a40557d5de
--- /dev/null
+++ b/src/test/omap_bench.hpp
@@ -0,0 +1,192 @@
+/*
+ * Generate latency statistics for a configurable number of object map write
+ * operations of configurable size.
+ *
+ * Created on: May 21, 2012
+ * Author: Eleanor Cawthon
+ *
+ * This is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2.1, as published by the Free Software
+ * Foundation. See file COPYING.
+ */
+
+#ifndef OMAP_BENCH_HPP_
+#define OMAP_BENCH_HPP_
+
+#include "common/Mutex.h"
+#include "common/Cond.h"
+#include "include/rados/librados.hpp"
+#include <string>
+#include<map>
+#include<cfloat>;
+
+using ceph::bufferlist;
+
+struct omap_bench_data{
+ double avg_latency;
+ double min_latency;
+ double max_latency;
+ double total_latency;
+ int started_ops;
+ int completed_ops;
+ std::map<int,int> freq_map;
+ pair<int,int> mode;
+ omap_bench_data(): avg_latency(0.0), min_latency(DBL_MAX), max_latency(0.0),
+ total_latency(0.0),started_ops(0),completed_ops(0),freq_map(),mode(){}
+
+};
+
+class OmapBench;
+
+typedef int (*omap_generator_t)(const int omap_entries, const int key_size,
+ const int value_size, std::map<std::string,bufferlist> * out_omap);
+typedef int (OmapBench::*test_t)(omap_generator_t omap_gen);
+
+
+class Writer{
+protected:
+ string oid;
+ utime_t begin_time;
+ utime_t end_time;
+ std::map<std::string,bufferlist> omap;
+ OmapBench *ob;
+ friend class OmapBench;
+public:
+ Writer(OmapBench *omap_bench);
+ virtual ~Writer(){};
+ virtual void start_time();
+ virtual void stop_time();
+ virtual double get_time();
+ virtual string get_oid();
+ virtual std::map<std::string,bufferlist> & get_omap();
+};
+
+class AioWriter : public Writer{
+protected:
+ librados::AioCompletion * aioc;
+ friend class OmapBench;
+
+public:
+ AioWriter(OmapBench *omap_bench);
+ ~AioWriter();
+ virtual librados::AioCompletion * get_aioc();
+ virtual void set_aioc(librados::callback_t complete,
+ librados::callback_t safe);
+};
+
+class OmapBench{
+protected:
+ librados::IoCtx io_ctx;
+ librados::Rados rados;
+ struct omap_bench_data data;
+ test_t test;
+ omap_generator_t omap_generator;
+
+ //aio things
+ Cond thread_is_free;
+ Mutex thread_is_free_lock;
+ Mutex data_lock;
+ int busythreads_count;
+ librados::callback_t comp;
+ librados::callback_t safe;
+
+ string pool_name;
+ string rados_id;
+ int threads;
+ int objects;
+ int omap_entries;
+ int omap_key_size;
+ int omap_value_size;
+ double increment;
+
+ friend class Writer;
+ friend class AioWriter;
+
+public:
+ OmapBench() : data(),busythreads_count(0),pool_name("data"),rados_id("admin"),
+ safe(aio_is_safe),omap_generator(generate_uniform_omap),
+ thread_is_free_lock("thread is free lock"), data_lock("data lock"),
+ threads(3),objects(100),omap_entries(10),omap_key_size(10),
+ omap_value_size(100),increment(10),test(&OmapBench::write_objects_in_parallel)
+ {};
+
+ /**
+ * Parses command line args, initializes rados and ioctx
+ */
+ int setup(int argc, const char** argv);
+
+ /**
+ * Callback for when an AioCompletion (called from an AioWriter)
+ * is safe. deletes the AioWriter that called it,
+ * Updates data, updates busythreads, and signals thread_is_free.
+ *
+ * @param c provided by aio_write - not used
+ * @param arg the AioWriter that contains this AioCompletion
+ */
+ static void aio_is_safe(rados_completion_t c, void *arg);
+
+ /**
+ * Generates a random string len characters long
+ */
+ static string random_string(int len);
+
+ /**
+ * Generates an omap with omap_entries entries, each with keys key_size
+ * characters long and with string values value_size characters long.
+ *
+ * @param out_map pointer to the map to be created
+ * @return error code
+ */
+ static int generate_uniform_omap(const int omap_entries, const int key_size,
+ const int value_size, std::map<std::string,bufferlist> * out_omap);
+
+ /**
+ * The same as generate_uniform_omap except that string lengths are picked
+ * randomly between 1 and the int arguments
+ */
+ static int generate_non_uniform_omap(const int omap_entries,
+ const int key_size,
+ const int value_size, std::map<std::string,bufferlist> * out_omap);
+
+ /**
+ * Writes an object with the specified AioWriter.
+ *
+ * @param aiow the AioWriter to write with
+ * @param omap the omap to write
+ * @post: an asynchronous omap_set is launched
+ */
+ int write_omap_asynchronously(AioWriter *aiow,
+ const std::map<std::string,bufferlist> &omap);
+
+ /*
+ * Uses aio_write to write omaps generated by omap_gen to OBJECTS objects
+ * using THREADS AioWriters at a time.
+ *
+ * @param omap_gen the method used to generate the omaps.
+ */
+ int write_objects_in_parallel(omap_generator_t omap_gen);
+
+ /*
+ * runs the test specified by test using the omap generator specified by
+ * omap_generator
+ *
+ * @return error code
+ */
+ int run();
+
+ /*
+ * Prints all keys and values for all omap entries for all objects
+ */
+ int print_written_omap();
+
+ /*
+ * Displays relevant constants and the histogram generated through a test
+ */
+ void print_results();
+};
+
+
+
+#endif /* OMAP_BENCH_HPP_ */
+