From a619fe986979c8c376c919f66208aa6746db20a2 Mon Sep 17 00:00:00 2001 From: Loic Dachary Date: Thu, 12 Dec 2013 00:34:35 +0100 Subject: [PATCH] osd: erasure code benchmark tool Implement the ceph_erasure_code_benchmark utility to: * load an erasure code plugin * loop over the encode/decode function using the parameters from the command line * print the number of bytes encoded/decoded and the time to process When decoding, random chunks ( as set with --erasures ) are lost on each run. For instance: $ ceph_erasure_code_benchmark \ --plugin jerasure \ --parameter erasure-code-directory=.libs \ --parameter erasure-code-technique=reed_sol_van \ --parameter erasure-code-k=2 \ --parameter erasure-code-m=2 \ --workload decode \ --erasures 2 \ --iterations 1000 0.964759 1048576 shows 1GB is decoded in 1second. It is intended to be used by other scripts to present a human readable output or detect performance regressions. Signed-off-by: Loic Dachary --- src/test/Makefile.am | 11 +- src/test/osd/ceph_erasure_code_benchmark.cc | 218 ++++++++++++++++++++ src/test/osd/ceph_erasure_code_benchmark.h | 38 ++++ 3 files changed, 264 insertions(+), 3 deletions(-) create mode 100644 src/test/osd/ceph_erasure_code_benchmark.cc create mode 100644 src/test/osd/ceph_erasure_code_benchmark.h diff --git a/src/test/Makefile.am b/src/test/Makefile.am index 3cd531ef674..0ca314fccc1 100644 --- a/src/test/Makefile.am +++ b/src/test/Makefile.am @@ -179,9 +179,13 @@ ceph_multi_stress_watch_SOURCES = \ ceph_multi_stress_watch_LDADD = $(LIBRADOS) $(CEPH_GLOBAL) bin_DEBUGPROGRAMS += ceph_multi_stress_watch - - - +ceph_erasure_code_benchmark_SOURCES = \ + test/osd/ceph_erasure_code_benchmark.cc +ceph_erasure_code_benchmark_LDADD = $(LIBOSD) $(LIBCOMMON) $(BOOST_PROGRAM_OPTIONS_LIBS) $(CEPH_GLOBAL) +if LINUX +ceph_erasure_code_benchmark_LDADD += -ldl +endif +bin_DEBUGPROGRAMS += ceph_erasure_code_benchmark ## System tests @@ -895,6 +899,7 @@ noinst_HEADERS += \ test/osd/RadosModel.h \ test/osd/Object.h \ test/osd/TestOpStat.h \ + test/osd/ceph_erasure_code_benchmark.h \ test/bench/distribution.h \ test/bench/rados_backend.h \ test/bench/rbd_backend.h \ diff --git a/src/test/osd/ceph_erasure_code_benchmark.cc b/src/test/osd/ceph_erasure_code_benchmark.cc new file mode 100644 index 00000000000..f4fdaf3e566 --- /dev/null +++ b/src/test/osd/ceph_erasure_code_benchmark.cc @@ -0,0 +1,218 @@ +// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- +// vim: ts=8 sw=2 smarttab +/* + * Ceph - scalable distributed file system + * + * Copyright (C) 2013 Cloudwatt + * + * Author: Loic Dachary + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "test/osd/ceph_erasure_code_benchmark.h" +#include "global/global_context.h" +#include "global/global_init.h" +#include "common/ceph_argparse.h" +#include "common/config.h" +#include "common/Clock.h" +#include "include/utime.h" +#include "osd/ErasureCodePlugin.h" + +namespace po = boost::program_options; + +int ErasureCodeBench::setup(int argc, char** argv) { + + po::options_description desc("Allowed options"); + desc.add_options() + ("help,h", "produce help message") + ("size,s", po::value()->default_value(1024 * 1024), + "size of the buffer to be encoded") + ("iterations,i", po::value()->default_value(1), + "number of encode/decode runs") + ("plugin,p", po::value()->default_value("jerasure"), + "erasure code plugin name") + ("workload,w", po::value()->default_value("encode"), + "run either encode or decode") + ("erasures,e", po::value()->default_value(1), + "number of erasures when decoding") + ("parameter,P", po::value >(), + "parameters") + ; + + po::variables_map vm; + po::parsed_options parsed = + po::command_line_parser(argc, argv).options(desc).allow_unregistered().run(); + po::store( + parsed, + vm); + po::notify(vm); + + vector ceph_options, def_args; + vector ceph_option_strings = po::collect_unrecognized( + parsed.options, po::include_positional); + ceph_options.reserve(ceph_option_strings.size()); + for (vector::iterator i = ceph_option_strings.begin(); + i != ceph_option_strings.end(); + ++i) { + ceph_options.push_back(i->c_str()); + } + + global_init( + &def_args, ceph_options, CEPH_ENTITY_TYPE_CLIENT, + CODE_ENVIRONMENT_UTILITY, + CINIT_FLAG_NO_DEFAULT_CONFIG_FILE); + common_init_finish(g_ceph_context); + g_ceph_context->_conf->apply_changes(NULL); + + if (vm.count("help")) { + cout << desc << std::endl; + return 1; + } + + if (vm.count("parameter")) { + const vector &p = vm["parameter"].as< vector >(); + for (vector::const_iterator i = p.begin(); + i != p.end(); + i++) { + std::vector strs; + boost::split(strs, *i, boost::is_any_of("=")); + if (strs.size() != 2) { + cerr << "--parameter " << *i << " ignored because it does not contain exactly one =" << endl; + } else { + parameters[strs[0]] = strs[1]; + } + } + } + + if (parameters.count("erasure-code-directory") == 0) + parameters["erasure-code-directory"] = ".libs"; + + in_size = vm["size"].as(); + max_iterations = vm["iterations"].as(); + plugin = vm["plugin"].as(); + workload = vm["workload"].as(); + erasures = vm["erasures"].as(); + + return 0; +} + +int ErasureCodeBench::run() { + ErasureCodePluginRegistry &instance = ErasureCodePluginRegistry::instance(); + instance.disable_dlclose = true; + + if (workload == "encode") + return encode(); + else + return decode(); +} + +int ErasureCodeBench::encode() +{ + ErasureCodePluginRegistry &instance = ErasureCodePluginRegistry::instance(); + ErasureCodeInterfaceRef erasure_code; + int code = instance.factory(plugin, parameters, &erasure_code); + if (code) + return code; + int k = atoi(parameters["erasure-code-k"].c_str()); + int m = atoi(parameters["erasure-code-m"].c_str()); + + bufferlist in; + in.append(string(in_size, 'X')); + set want_to_encode; + for (int i = 0; i < k + m; i++) { + want_to_encode.insert(i); + } + utime_t begin_time = ceph_clock_now(g_ceph_context); + for (int i = 0; i < max_iterations; i++) { + map encoded; + code = erasure_code->encode(want_to_encode, in, &encoded); + if (code) + return code; + } + utime_t end_time = ceph_clock_now(g_ceph_context); + cout << (end_time - begin_time) << "\t" << (max_iterations * (in_size / 1024)) << endl; + return 0; +} + +int ErasureCodeBench::decode() +{ + ErasureCodePluginRegistry &instance = ErasureCodePluginRegistry::instance(); + ErasureCodeInterfaceRef erasure_code; + int code = instance.factory(plugin, parameters, &erasure_code); + if (code) + return code; + int k = atoi(parameters["erasure-code-k"].c_str()); + int m = atoi(parameters["erasure-code-m"].c_str()); + + bufferlist in; + in.append(string(in_size, 'X')); + + set want_to_encode; + for (int i = 0; i < k + m; i++) { + want_to_encode.insert(i); + } + + map encoded; + code = erasure_code->encode(want_to_encode, in, &encoded); + if (code) + return code; + + set want_to_read = want_to_encode; + + utime_t begin_time = ceph_clock_now(g_ceph_context); + for (int i = 0; i < max_iterations; i++) { + map chunks = encoded; + for (int j = 0; j < erasures; j++) { + int erasure; + do { + erasure = rand() % ( k + m ); + } while(chunks.count(erasure) == 0); + chunks.erase(erasure); + } + map decoded; + code = erasure_code->decode(want_to_read, chunks, &decoded); + if (code) + return code; + } + utime_t end_time = ceph_clock_now(g_ceph_context); + cout << (end_time - begin_time) << "\t" << (max_iterations * (in_size / 1024)) << endl; + return 0; +} + +int main(int argc, char** argv) { + ErasureCodeBench ecbench; + int err = ecbench.setup(argc, argv); + if (err) + return err; + return ecbench.run(); +} + +/* + * Local Variables: + * compile-command: "cd ../.. ; make -j4 && + * make ceph_erasure_code_benchmark && + * valgrind --tool=memcheck --leak-check=full \ + * ./ceph_erasure_code_benchmark \ + * --plugin jerasure \ + * --parameter erasure-code-directory=.libs \ + * --parameter erasure-code-technique=reed_sol_van \ + * --parameter erasure-code-k=2 \ + * --parameter erasure-code-m=2 \ + * --iterations 1 + * " + * End: + */ diff --git a/src/test/osd/ceph_erasure_code_benchmark.h b/src/test/osd/ceph_erasure_code_benchmark.h new file mode 100644 index 00000000000..8ea60f94f4c --- /dev/null +++ b/src/test/osd/ceph_erasure_code_benchmark.h @@ -0,0 +1,38 @@ +// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- +// vim: ts=8 sw=2 smarttab +/* + * Ceph - scalable distributed file system + * + * Copyright (C) 2013 Cloudwatt + * + * Author: Loic Dachary + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + */ + +#ifndef CEPH_ERASURE_CODE_BENCHMARK_H +#define CEPH_ERASURE_CODE_BENCHMARK_H + +#include + +using namespace std; + +class ErasureCodeBench { + int in_size; + int max_iterations; + string plugin; + int erasures; + string workload; + map parameters; +public: + int setup(int argc, char** argv); + int run(); + int decode(); + int encode(); +}; + +#endif