From: Dongsheng Yang Date: Fri, 13 May 2016 08:57:32 +0000 (-0400) Subject: tools/rbd: introduce a simple bench for read X-Git-Tag: v11.0.0~90^2 X-Git-Url: http://git-server-git.apps.pok.os.sepia.ceph.com/?a=commitdiff_plain;h=23a135d2a8a9fc5acb8d833419d564eac8f3933b;p=ceph-ci.git tools/rbd: introduce a simple bench for read Currently, we have a bench-write for in rbd tool, but we don't have a simple bench for read. This patch add a new subcommand of bench, with an option of --io-type. Then we can do a simple bench for write or read. Signed-off-by: Dongsheng Yang --- diff --git a/doc/man/8/rbd.rst b/doc/man/8/rbd.rst index c9136f699da..9ba5442e7a1 100644 --- a/doc/man/8/rbd.rst +++ b/doc/man/8/rbd.rst @@ -369,6 +369,13 @@ Commands Release a lock on an image. The lock id and locker are as output by lock ls. +:command:`bench` --io-type [--io-size *size-in-B/K/M/G/T*] [--io-threads *num-ios-in-flight*] [--io-total *total-size-for-IO-in-B/K/M/G/T*] [--io-pattern seq | rand] *image-spec* + Generate a series of IOs to the image and measure the IO throughput and + latency. Defaults are: --io-size 4096, --io-threads 16, --io-total 1G, + --io-pattern seq. + + NOTE: --io-type must be specified. + Image and snap specs ==================== diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 1aa9ddad906..93416009722 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1261,7 +1261,7 @@ if(${WITH_RBD}) tools/rbd/OptionPrinter.cc tools/rbd/Shell.cc tools/rbd/Utils.cc - tools/rbd/action/BenchWrite.cc + tools/rbd/action/Bench.cc tools/rbd/action/Children.cc tools/rbd/action/Clone.cc tools/rbd/action/Copy.cc diff --git a/src/test/cli-integration/rbd/formatted-output.t b/src/test/cli-integration/rbd/formatted-output.t index af18b267422..b05cc583f55 100644 --- a/src/test/cli-integration/rbd/formatted-output.t +++ b/src/test/cli-integration/rbd/formatted-output.t @@ -33,7 +33,7 @@ clone $ rbd clone --image-feature layering,exclusive-lock,object-map,fast-diff bar@snap rbd_other/child $ rbd snap create rbd_other/child@snap $ rbd flatten rbd_other/child 2> /dev/null - $ rbd bench-write rbd_other/child --io-pattern seq --io-total 1B > /dev/null 2>&1 + $ rbd bench rbd_other/child --io-type write --io-pattern seq --io-total 1B > /dev/null 2>&1 $ rbd clone bar@snap rbd_other/deep-flatten-child $ rbd snap create rbd_other/deep-flatten-child@snap $ rbd flatten rbd_other/deep-flatten-child 2> /dev/null diff --git a/src/test/cli/rbd/help.t b/src/test/cli/rbd/help.t index 49f3f141097..d26075837bc 100644 --- a/src/test/cli/rbd/help.t +++ b/src/test/cli/rbd/help.t @@ -5,7 +5,7 @@ Positional arguments: - bench-write Simple write benchmark. + bench Simple benchmark. children Display children of snapshot. clone Clone a snapshot into a COW child image. copy (cp) Copy src image to dest. @@ -96,13 +96,13 @@ See 'rbd help ' for help on a specific command. $ rbd help | grep '^ [a-z]' | sed 's/^ \([a-z -]*[a-z]\).*/\1/g' | while read -r line; do echo rbd help $line ; rbd help $line; done - rbd help bench-write - usage: rbd bench-write [--pool ] [--image ] [--io-size ] - [--io-threads ] [--io-total ] - [--io-pattern ] - + rbd help bench + usage: rbd bench [--pool ] [--image ] [--io-size ] + [--io-threads ] [--io-total ] + [--io-pattern ] --io-type + - Simple write benchmark. + Simple benchmark. Positional arguments image specification @@ -111,10 +111,11 @@ Optional arguments -p [ --pool ] arg pool name --image arg image name - --io-size arg write size (in B/K/M/G/T) + --io-size arg IO size (in B/K/M/G/T) --io-threads arg ios in flight - --io-total arg total size to write (in B/K/M/G/T) - --io-pattern arg write pattern (rand or seq) + --io-total arg total size for IO (in B/K/M/G/T) + --io-pattern arg IO pattern (rand or seq) + --io-type arg IO type (read or write) rbd help children usage: rbd children [--pool ] [--image ] [--snap ] diff --git a/src/test/cli/rbd/invalid-snap-usage.t b/src/test/cli/rbd/invalid-snap-usage.t index 9b8c5946461..5f98c1a243e 100644 --- a/src/test/cli/rbd/invalid-snap-usage.t +++ b/src/test/cli/rbd/invalid-snap-usage.t @@ -88,7 +88,7 @@ $ rbd lock remove foo@snap id client.1234 rbd: snapname specified for a command that doesn't use it [22] - $ rbd bench-write foo@snap + $ rbd bench foo@snap --io-type write rbd: snapname specified for a command that doesn't use it [22] diff --git a/src/test/cli/rbd/not-enough-args.t b/src/test/cli/rbd/not-enough-args.t index 72adf186a97..3cc98b28996 100644 --- a/src/test/cli/rbd/not-enough-args.t +++ b/src/test/cli/rbd/not-enough-args.t @@ -187,7 +187,7 @@ $ rbd lock remove foo id rbd: locker was not specified [22] - $ rbd bench-write + $ rbd bench --io-type write rbd: image name was not specified [22] $ rbd mirror pool enable rbd diff --git a/src/tools/Makefile-client.am b/src/tools/Makefile-client.am index 906ea48439f..6bb86a6ab46 100644 --- a/src/tools/Makefile-client.am +++ b/src/tools/Makefile-client.am @@ -30,7 +30,7 @@ rbd_SOURCES = \ tools/rbd/OptionPrinter.cc \ tools/rbd/Shell.cc \ tools/rbd/Utils.cc \ - tools/rbd/action/BenchWrite.cc \ + tools/rbd/action/Bench.cc \ tools/rbd/action/Children.cc \ tools/rbd/action/Clone.cc \ tools/rbd/action/Group.cc \ diff --git a/src/tools/rbd/action/Bench.cc b/src/tools/rbd/action/Bench.cc new file mode 100644 index 00000000000..84543375f24 --- /dev/null +++ b/src/tools/rbd/action/Bench.cc @@ -0,0 +1,414 @@ +// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- +// vim: ts=8 sw=2 smarttab + +#include "tools/rbd/ArgumentTypes.h" +#include "tools/rbd/Shell.h" +#include "tools/rbd/Utils.h" +#include "common/errno.h" +#include "common/strtol.h" +#include "common/Cond.h" +#include "common/Mutex.h" +#include +#include +#include +#include +#include + +namespace rbd { +namespace action { +namespace bench { + +namespace at = argument_types; +namespace po = boost::program_options; + +namespace { + +enum io_type_t { + IO_TYPE_READ = 0, + IO_TYPE_WRITE, + + IO_TYPE_NUM, +}; + +struct IOType {}; +struct Size {}; +struct IOPattern {}; + +void validate(boost::any& v, const std::vector& values, + Size *target_type, int) { + po::validators::check_first_occurrence(v); + const std::string &s = po::validators::get_single_string(values); + + std::string parse_error; + uint64_t size = strict_sistrtoll(s.c_str(), &parse_error); + if (!parse_error.empty()) { + throw po::validation_error(po::validation_error::invalid_option_value); + } + v = boost::any(size); +} + +void validate(boost::any& v, const std::vector& values, + IOPattern *target_type, int) { + po::validators::check_first_occurrence(v); + const std::string &s = po::validators::get_single_string(values); + if (s == "rand") { + v = boost::any(true); + } else if (s == "seq") { + v = boost::any(false); + } else { + throw po::validation_error(po::validation_error::invalid_option_value); + } +} + +io_type_t get_io_type(string io_type_string) { + if (io_type_string == "read") + return IO_TYPE_READ; + else if (io_type_string == "write") + return IO_TYPE_WRITE; + else + return IO_TYPE_NUM; +} + +void validate(boost::any& v, const std::vector& values, + IOType *target_type, int) { + po::validators::check_first_occurrence(v); + const std::string &s = po::validators::get_single_string(values); + io_type_t io_type = get_io_type(s); + if (io_type >= IO_TYPE_NUM) + throw po::validation_error(po::validation_error::invalid_option_value); + else + v = boost::any(io_type); +} + +} // anonymous namespace + +static void rbd_bencher_completion(void *c, void *pc); +struct rbd_bencher; + +struct bencher_completer { + rbd_bencher *bencher; + bufferlist *bl; + +public: + bencher_completer(rbd_bencher *bencher, bufferlist *bl) + : bencher(bencher), bl(bl) + { } + + ~bencher_completer() + { + if (bl) + delete bl; + } +}; + +struct rbd_bencher { + librbd::Image *image; + Mutex lock; + Cond cond; + int in_flight; + io_type_t io_type; + uint64_t io_size; + bufferlist write_bl; + + explicit rbd_bencher(librbd::Image *i, io_type_t io_type, uint64_t io_size) + : image(i), + lock("rbd_bencher::lock"), + in_flight(0), + io_type(io_type), + io_size(io_size) + { + if (io_type == IO_TYPE_WRITE) { + bufferptr bp(io_size); + memset(bp.c_str(), rand() & 0xff, io_size); + write_bl.push_back(bp); + } + } + + + bool start_io(int max, uint64_t off, uint64_t len, int op_flags) + { + { + Mutex::Locker l(lock); + if (in_flight >= max) + return false; + in_flight++; + } + + librbd::RBD::AioCompletion *c; + if (io_type == IO_TYPE_READ) { + bufferlist *read_bl = new bufferlist(); + c = new librbd::RBD::AioCompletion((void *)(new bencher_completer(this, read_bl)), + rbd_bencher_completion); + image->aio_read2(off, len, *read_bl, c, op_flags); + } else if (io_type == IO_TYPE_WRITE) { + c = new librbd::RBD::AioCompletion((void *)(new bencher_completer(this, NULL)), + rbd_bencher_completion); + image->aio_write2(off, len, write_bl, c, op_flags); + } else { + assert(0 == "Invalid io_type"); + } + //cout << "start " << c << " at " << off << "~" << len << std::endl; + return true; + } + + void wait_for(int max) { + Mutex::Locker l(lock); + while (in_flight > max) { + utime_t dur; + dur.set_from_double(.2); + cond.WaitInterval(g_ceph_context, lock, dur); + } + } + +}; + +void rbd_bencher_completion(void *vc, void *pc) +{ + librbd::RBD::AioCompletion *c = (librbd::RBD::AioCompletion *)vc; + bencher_completer *bc = static_cast(pc); + rbd_bencher *b = bc->bencher; + //cout << "complete " << c << std::endl; + int ret = c->get_return_value(); + if (b->io_type == IO_TYPE_WRITE && ret != 0) { + cout << "write error: " << cpp_strerror(ret) << std::endl; + exit(ret < 0 ? -ret : ret); + } else if (b->io_type == IO_TYPE_READ && (unsigned int)ret != b->io_size) { + cout << "read error: " << cpp_strerror(ret) << std::endl; + exit(ret < 0 ? -ret : ret); + } + b->lock.Lock(); + b->in_flight--; + b->cond.Signal(); + b->lock.Unlock(); + c->release(); + delete bc; +} + +int do_bench(librbd::Image& image, io_type_t io_type, + uint64_t io_size, uint64_t io_threads, + uint64_t io_bytes, bool random) +{ + rbd_bencher b(&image, io_type, io_size); + + std::cout << "bench " + << " type " << (io_type == IO_TYPE_READ ? "read" : "write") + << " io_size " << io_size + << " io_threads " << io_threads + << " bytes " << io_bytes + << " pattern " << (random ? "random" : "sequential") + << std::endl; + + srand(time(NULL) % (unsigned long) -1); + + utime_t start = ceph_clock_now(NULL); + utime_t last; + unsigned ios = 0; + + uint64_t size = 0; + image.size(&size); + + vector thread_offset; + uint64_t i; + uint64_t start_pos; + + // disturb all thread's offset, used by seq IO + for (i = 0; i < io_threads; i++) { + start_pos = (rand() % (size / io_size)) * io_size; + thread_offset.push_back(start_pos); + } + + const int WINDOW_SIZE = 5; + typedef boost::accumulators::accumulator_set< + double, boost::accumulators::stats< + boost::accumulators::tag::rolling_sum> > RollingSum; + + RollingSum time_acc( + boost::accumulators::tag::rolling_window::window_size = WINDOW_SIZE); + RollingSum ios_acc( + boost::accumulators::tag::rolling_window::window_size = WINDOW_SIZE); + RollingSum off_acc( + boost::accumulators::tag::rolling_window::window_size = WINDOW_SIZE); + uint64_t cur_ios = 0; + uint64_t cur_off = 0; + + int op_flags; + if (random) { + op_flags = LIBRADOS_OP_FLAG_FADVISE_RANDOM; + } else { + op_flags = LIBRADOS_OP_FLAG_FADVISE_SEQUENTIAL; + } + + printf(" SEC OPS OPS/SEC BYTES/SEC\n"); + uint64_t off; + for (off = 0; off < io_bytes; ) { + b.wait_for(io_threads - 1); + i = 0; + while (i < io_threads && off < io_bytes) { + if (random) { + thread_offset[i] = (rand() % (size / io_size)) * io_size; + } else { + thread_offset[i] += io_size; + if (thread_offset[i] + io_size > size) + thread_offset[i] = 0; + } + + if (!b.start_io(io_threads, thread_offset[i], io_size, op_flags)) + break; + + ++i; + ++ios; + off += io_size; + + ++cur_ios; + cur_off += io_size; + } + + utime_t now = ceph_clock_now(NULL); + utime_t elapsed = now - start; + if (last.is_zero()) { + last = elapsed; + } else if (elapsed.sec() != last.sec()) { + time_acc(elapsed - last); + ios_acc(static_cast(cur_ios)); + off_acc(static_cast(cur_off)); + cur_ios = 0; + cur_off = 0; + + double time_sum = boost::accumulators::rolling_sum(time_acc); + printf("%5d %8d %8.2lf %8.2lf\n", + (int)elapsed, + (int)(ios - io_threads), + boost::accumulators::rolling_sum(ios_acc) / time_sum, + boost::accumulators::rolling_sum(off_acc) / time_sum); + last = elapsed; + } + } + b.wait_for(0); + int r = image.flush(); + if (r < 0) { + std::cerr << "Error flushing data at the end: " << cpp_strerror(r) + << std::endl; + } + + utime_t now = ceph_clock_now(NULL); + double elapsed = now - start; + + printf("elapsed: %5d ops: %8d ops/sec: %8.2lf bytes/sec: %8.2lf\n", + (int)elapsed, ios, (double)ios / elapsed, (double)off / elapsed); + + return 0; +} + +void add_bench_common_options(po::options_description *positional, + po::options_description *options) { + at::add_image_spec_options(positional, options, at::ARGUMENT_MODIFIER_NONE); + + options->add_options() + ("io-size", po::value(), "IO size (in B/K/M/G/T)") + ("io-threads", po::value(), "ios in flight") + ("io-total", po::value(), "total size for IO (in B/K/M/G/T)") + ("io-pattern", po::value(), "IO pattern (rand or seq)"); +} + +void get_arguments_for_write(po::options_description *positional, + po::options_description *options) { + add_bench_common_options(positional, options); +} + +void get_arguments_for_bench(po::options_description *positional, + po::options_description *options) { + add_bench_common_options(positional, options); + + options->add_options() + ("io-type", po::value()->required(), "IO type (read or write)"); +} + +int bench_execute(const po::variables_map &vm, io_type_t bench_io_type) { + size_t arg_index = 0; + std::string pool_name; + std::string image_name; + std::string snap_name; + utils::SnapshotPresence snap_presence = utils::SNAPSHOT_PRESENCE_NONE; + if (bench_io_type == IO_TYPE_READ) + snap_presence = utils::SNAPSHOT_PRESENCE_PERMITTED; + + int r = utils::get_pool_image_snapshot_names( + vm, at::ARGUMENT_MODIFIER_NONE, &arg_index, &pool_name, &image_name, + &snap_name, snap_presence, utils::SPEC_VALIDATION_NONE); + if (r < 0) { + return r; + } + + uint64_t bench_io_size; + if (vm.count("io-size")) { + bench_io_size = vm["io-size"].as(); + } else { + bench_io_size = 4096; + } + + uint32_t bench_io_threads; + if (vm.count("io-threads")) { + bench_io_threads = vm["io-threads"].as(); + } else { + bench_io_threads = 16; + } + + uint64_t bench_bytes; + if (vm.count("io-total")) { + bench_bytes = vm["io-total"].as(); + } else { + bench_bytes = 1 << 30; + } + + bool bench_random; + if (vm.count("io-pattern")) { + bench_random = vm["io-pattern"].as(); + } else { + bench_random = false; + } + + librados::Rados rados; + librados::IoCtx io_ctx; + librbd::Image image; + r = utils::init_and_open_image(pool_name, image_name, "", false, &rados, + &io_ctx, &image); + if (r < 0) { + return r; + } + + r = do_bench(image, bench_io_type, bench_io_size, bench_io_threads, + bench_bytes, bench_random); + if (r < 0) { + std::cerr << "do_bench failed: " << cpp_strerror(r) << std::endl; + return r; + } + return 0; +} + +int execute_for_write(const po::variables_map &vm) { + std::cerr << "rbd: bench-write is deprecated, use rbd bench --io-type write ..." << std::endl; + return bench_execute(vm, IO_TYPE_WRITE); +} + +int execute_for_bench(const po::variables_map &vm) { + io_type_t bench_io_type; + if (vm.count("io-type")) { + bench_io_type = vm["io-type"].as(); + } else { + std::cerr << "rbd: --io-type must be specified." << std::endl; + return -EINVAL; + } + + return bench_execute(vm, bench_io_type); +} + +Shell::Action action_write( + {"bench-write"}, {}, "Simple write benchmark. (Deprecated, please use `rbd bench --io-type write` instead.)", + "", &get_arguments_for_write, &execute_for_write, false); + +Shell::Action action_bench( + {"bench"}, {}, "Simple benchmark.", "", &get_arguments_for_bench, &execute_for_bench); + +} // namespace bench +} // namespace action +} // namespace rbd diff --git a/src/tools/rbd/action/BenchWrite.cc b/src/tools/rbd/action/BenchWrite.cc deleted file mode 100644 index 6691a28974a..00000000000 --- a/src/tools/rbd/action/BenchWrite.cc +++ /dev/null @@ -1,310 +0,0 @@ -// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- -// vim: ts=8 sw=2 smarttab - -#include "tools/rbd/ArgumentTypes.h" -#include "tools/rbd/Shell.h" -#include "tools/rbd/Utils.h" -#include "common/errno.h" -#include "common/strtol.h" -#include "common/Cond.h" -#include "common/Mutex.h" -#include -#include -#include -#include -#include - -namespace rbd { -namespace action { -namespace bench_write { - -namespace at = argument_types; -namespace po = boost::program_options; - -namespace { - -struct Size {}; -struct IOPattern {}; - -void validate(boost::any& v, const std::vector& values, - Size *target_type, int) { - po::validators::check_first_occurrence(v); - const std::string &s = po::validators::get_single_string(values); - - std::string parse_error; - uint64_t size = strict_sistrtoll(s.c_str(), &parse_error); - if (!parse_error.empty()) { - throw po::validation_error(po::validation_error::invalid_option_value); - } - v = boost::any(size); -} - -void validate(boost::any& v, const std::vector& values, - IOPattern *target_type, int) { - po::validators::check_first_occurrence(v); - const std::string &s = po::validators::get_single_string(values); - if (s == "rand") { - v = boost::any(true); - } else if (s == "seq") { - v = boost::any(false); - } else { - throw po::validation_error(po::validation_error::invalid_option_value); - } - -} - -} // anonymous namespace - -static void rbd_bencher_completion(void *c, void *pc); -struct rbd_bencher; - -struct rbd_bencher { - librbd::Image *image; - Mutex lock; - Cond cond; - int in_flight; - - explicit rbd_bencher(librbd::Image *i) - : image(i), - lock("rbd_bencher::lock"), - in_flight(0) - { } - - bool start_write(int max, uint64_t off, uint64_t len, bufferlist& bl, - int op_flags) - { - { - Mutex::Locker l(lock); - if (in_flight >= max) - return false; - in_flight++; - } - librbd::RBD::AioCompletion *c = - new librbd::RBD::AioCompletion((void *)this, rbd_bencher_completion); - image->aio_write2(off, len, bl, c, op_flags); - //cout << "start " << c << " at " << off << "~" << len << std::endl; - return true; - } - - void wait_for(int max) { - Mutex::Locker l(lock); - while (in_flight > max) { - utime_t dur; - dur.set_from_double(.2); - cond.WaitInterval(g_ceph_context, lock, dur); - } - } - -}; - -void rbd_bencher_completion(void *vc, void *pc) -{ - librbd::RBD::AioCompletion *c = (librbd::RBD::AioCompletion *)vc; - rbd_bencher *b = static_cast(pc); - //cout << "complete " << c << std::endl; - int ret = c->get_return_value(); - if (ret != 0) { - cout << "write error: " << cpp_strerror(ret) << std::endl; - exit(ret < 0 ? -ret : ret); - } - b->lock.Lock(); - b->in_flight--; - b->cond.Signal(); - b->lock.Unlock(); - c->release(); -} - -int do_bench_write(librbd::Image& image, uint64_t io_size, - uint64_t io_threads, uint64_t io_bytes, - bool random) -{ - rbd_bencher b(&image); - - std::cout << "bench-write " - << " io_size " << io_size - << " io_threads " << io_threads - << " bytes " << io_bytes - << " pattern " << (random ? "random" : "sequential") - << std::endl; - - srand(time(NULL) % (unsigned long) -1); - - bufferptr bp(io_size); - memset(bp.c_str(), rand() & 0xff, io_size); - bufferlist bl; - bl.push_back(bp); - - utime_t start = ceph_clock_now(NULL); - utime_t last; - unsigned ios = 0; - - uint64_t size = 0; - image.size(&size); - - vector thread_offset; - uint64_t i; - uint64_t start_pos; - - // disturb all thread's offset, used by seq write - for (i = 0; i < io_threads; i++) { - start_pos = (rand() % (size / io_size)) * io_size; - thread_offset.push_back(start_pos); - } - - const int WINDOW_SIZE = 5; - typedef boost::accumulators::accumulator_set< - double, boost::accumulators::stats< - boost::accumulators::tag::rolling_sum> > RollingSum; - - RollingSum time_acc( - boost::accumulators::tag::rolling_window::window_size = WINDOW_SIZE); - RollingSum ios_acc( - boost::accumulators::tag::rolling_window::window_size = WINDOW_SIZE); - RollingSum off_acc( - boost::accumulators::tag::rolling_window::window_size = WINDOW_SIZE); - uint64_t cur_ios = 0; - uint64_t cur_off = 0; - - int op_flags; - if (random) { - op_flags = LIBRADOS_OP_FLAG_FADVISE_RANDOM; - } else { - op_flags = LIBRADOS_OP_FLAG_FADVISE_SEQUENTIAL; - } - - printf(" SEC OPS OPS/SEC BYTES/SEC\n"); - uint64_t off; - for (off = 0; off < io_bytes; ) { - b.wait_for(io_threads - 1); - i = 0; - while (i < io_threads && off < io_bytes) { - if (random) { - thread_offset[i] = (rand() % (size / io_size)) * io_size; - } else { - thread_offset[i] += io_size; - if (thread_offset[i] + io_size > size) - thread_offset[i] = 0; - } - - if (!b.start_write(io_threads, thread_offset[i], io_size, bl, op_flags)) - break; - - ++i; - ++ios; - off += io_size; - - ++cur_ios; - cur_off += io_size; - } - - utime_t now = ceph_clock_now(NULL); - utime_t elapsed = now - start; - if (last.is_zero()) { - last = elapsed; - } else if (elapsed.sec() != last.sec()) { - time_acc(elapsed - last); - ios_acc(static_cast(cur_ios)); - off_acc(static_cast(cur_off)); - cur_ios = 0; - cur_off = 0; - - double time_sum = boost::accumulators::rolling_sum(time_acc); - printf("%5d %8d %8.2lf %8.2lf\n", - (int)elapsed, - (int)(ios - io_threads), - boost::accumulators::rolling_sum(ios_acc) / time_sum, - boost::accumulators::rolling_sum(off_acc) / time_sum); - last = elapsed; - } - } - b.wait_for(0); - int r = image.flush(); - if (r < 0) { - std::cerr << "Error flushing data at the end: " << cpp_strerror(r) - << std::endl; - } - - utime_t now = ceph_clock_now(NULL); - double elapsed = now - start; - - printf("elapsed: %5d ops: %8d ops/sec: %8.2lf bytes/sec: %8.2lf\n", - (int)elapsed, ios, (double)ios / elapsed, (double)off / elapsed); - - return 0; -} - -void get_arguments(po::options_description *positional, - po::options_description *options) { - at::add_image_spec_options(positional, options, at::ARGUMENT_MODIFIER_NONE); - // TODO - options->add_options() - ("io-size", po::value(), "write size (in B/K/M/G/T)") - ("io-threads", po::value(), "ios in flight") - ("io-total", po::value(), "total size to write (in B/K/M/G/T)") - ("io-pattern", po::value(), "write pattern (rand or seq)"); -} - -int execute(const po::variables_map &vm) { - size_t arg_index = 0; - std::string pool_name; - std::string image_name; - std::string snap_name; - int r = utils::get_pool_image_snapshot_names( - vm, at::ARGUMENT_MODIFIER_NONE, &arg_index, &pool_name, &image_name, - &snap_name, utils::SNAPSHOT_PRESENCE_NONE, utils::SPEC_VALIDATION_NONE); - if (r < 0) { - return r; - } - - uint64_t bench_io_size; - if (vm.count("io-size")) { - bench_io_size = vm["io-size"].as(); - } else { - bench_io_size = 4096; - } - - uint32_t bench_io_threads; - if (vm.count("io-threads")) { - bench_io_threads = vm["io-threads"].as(); - } else { - bench_io_threads = 16; - } - - uint64_t bench_bytes; - if (vm.count("io-total")) { - bench_bytes = vm["io-total"].as(); - } else { - bench_bytes = 1 << 30; - } - - bool bench_random; - if (vm.count("io-pattern")) { - bench_random = vm["io-pattern"].as(); - } else { - bench_random = false; - } - - librados::Rados rados; - librados::IoCtx io_ctx; - librbd::Image image; - r = utils::init_and_open_image(pool_name, image_name, "", false, &rados, - &io_ctx, &image); - if (r < 0) { - return r; - } - - r = do_bench_write(image, bench_io_size, bench_io_threads, bench_bytes, - bench_random); - if (r < 0) { - std::cerr << "bench-write failed: " << cpp_strerror(r) << std::endl; - return r; - } - return 0; -} - -Shell::Action action( - {"bench-write"}, {}, "Simple write benchmark.", "", &get_arguments, &execute, false); - -} // namespace bench_write -} // namespace action -} // namespace rbd