From e6918187568dbd01842d8d1d2c808ce16a894239 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sun, 21 Apr 2024 13:54:28 +0200 Subject: Adding upstream version 18.2.2. Signed-off-by: Daniel Baumann --- .../erasure-code/ceph_erasure_code_benchmark.cc | 354 +++++++++++++++++++++ 1 file changed, 354 insertions(+) create mode 100644 src/test/erasure-code/ceph_erasure_code_benchmark.cc (limited to 'src/test/erasure-code/ceph_erasure_code_benchmark.cc') diff --git a/src/test/erasure-code/ceph_erasure_code_benchmark.cc b/src/test/erasure-code/ceph_erasure_code_benchmark.cc new file mode 100644 index 000000000..c86e58697 --- /dev/null +++ b/src/test/erasure-code/ceph_erasure_code_benchmark.cc @@ -0,0 +1,354 @@ +// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- +// vim: ts=8 sw=2 smarttab +/* + * Ceph distributed storage system + * + * Copyright (C) 2013,2014 Cloudwatt + * Copyright (C) 2014 Red Hat + * + * 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 "global/global_context.h" +#include "global/global_init.h" +#include "common/ceph_argparse.h" +#include "common/ceph_context.h" +#include "common/config.h" +#include "common/Clock.h" +#include "include/utime.h" +#include "erasure-code/ErasureCodePlugin.h" +#include "erasure-code/ErasureCode.h" +#include "ceph_erasure_code_benchmark.h" + +using std::endl; +using std::cerr; +using std::cout; +using std::map; +using std::set; +using std::string; +using std::stringstream; +using std::vector; + +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") + ("verbose,v", "explain what happens") + ("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") + ("erased", po::value >(), + "erased chunk (repeat if more than one chunk is erased)") + ("erasures-generation,E", po::value()->default_value("random"), + "If set to 'random', pick the number of chunks to recover (as specified by " + " --erasures) at random. If set to 'exhaustive' try all combinations of erasures " + " (i.e. k=4,m=3 with one erasure will try to recover from the erasure of " + " the first chunk, then the second etc.)") + ("parameter,P", po::value >(), + "add a parameter to the erasure code profile") + ; + + 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; + 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()); + } + + cct = global_init( + NULL, 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(nullptr); + + 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 { + profile[strs[0]] = strs[1]; + } + } + } + + in_size = vm["size"].as(); + max_iterations = vm["iterations"].as(); + plugin = vm["plugin"].as(); + workload = vm["workload"].as(); + erasures = vm["erasures"].as(); + if (vm.count("erasures-generation") > 0 && + vm["erasures-generation"].as() == "exhaustive") + exhaustive_erasures = true; + else + exhaustive_erasures = false; + if (vm.count("erased") > 0) + erased = vm["erased"].as >(); + + try { + k = stoi(profile["k"]); + m = stoi(profile["m"]); + } catch (const std::logic_error& e) { + cout << "Invalid k and/or m: k=" << profile["k"] << ", m=" << profile["m"] + << " (" << e.what() << ")" << endl; + return -EINVAL; + } + if (k <= 0) { + cout << "parameter k is " << k << ". But k needs to be > 0." << endl; + return -EINVAL; + } else if ( m < 0 ) { + cout << "parameter m is " << m << ". But m needs to be >= 0." << endl; + return -EINVAL; + } + + verbose = vm.count("verbose") > 0 ? true : false; + + 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; + stringstream messages; + int code = instance.factory(plugin, + g_conf().get_val("erasure_code_dir"), + profile, &erasure_code, &messages); + if (code) { + cerr << messages.str() << endl; + return code; + } + + bufferlist in; + in.append(string(in_size, 'X')); + in.rebuild_aligned(ErasureCode::SIMD_ALIGN); + set want_to_encode; + for (int i = 0; i < k + m; i++) { + want_to_encode.insert(i); + } + utime_t begin_time = ceph_clock_now(); + for (int i = 0; i < max_iterations; i++) { + std::map encoded; + code = erasure_code->encode(want_to_encode, in, &encoded); + if (code) + return code; + } + utime_t end_time = ceph_clock_now(); + cout << (end_time - begin_time) << "\t" << (max_iterations * (in_size / 1024)) << endl; + return 0; +} + +static void display_chunks(const map &chunks, + unsigned int chunk_count) { + cout << "chunks "; + for (unsigned int chunk = 0; chunk < chunk_count; chunk++) { + if (chunks.count(chunk) == 0) { + cout << "(" << chunk << ")"; + } else { + cout << " " << chunk << " "; + } + cout << " "; + } + cout << "(X) is an erased chunk" << endl; +} + +int ErasureCodeBench::decode_erasures(const map &all_chunks, + const map &chunks, + unsigned i, + unsigned want_erasures, + ErasureCodeInterfaceRef erasure_code) +{ + int code = 0; + + if (want_erasures == 0) { + if (verbose) + display_chunks(chunks, erasure_code->get_chunk_count()); + set want_to_read; + for (unsigned int chunk = 0; chunk < erasure_code->get_chunk_count(); chunk++) + if (chunks.count(chunk) == 0) + want_to_read.insert(chunk); + + map decoded; + code = erasure_code->decode(want_to_read, chunks, &decoded, 0); + if (code) + return code; + for (set::iterator chunk = want_to_read.begin(); + chunk != want_to_read.end(); + ++chunk) { + if (all_chunks.find(*chunk)->second.length() != decoded[*chunk].length()) { + cerr << "chunk " << *chunk << " length=" << all_chunks.find(*chunk)->second.length() + << " decoded with length=" << decoded[*chunk].length() << endl; + return -1; + } + bufferlist tmp = all_chunks.find(*chunk)->second; + if (!tmp.contents_equal(decoded[*chunk])) { + cerr << "chunk " << *chunk + << " content and recovered content are different" << endl; + return -1; + } + } + return 0; + } + + for (; i < erasure_code->get_chunk_count(); i++) { + map one_less = chunks; + one_less.erase(i); + code = decode_erasures(all_chunks, one_less, i + 1, want_erasures - 1, erasure_code); + if (code) + return code; + } + + return 0; +} + +int ErasureCodeBench::decode() +{ + ErasureCodePluginRegistry &instance = ErasureCodePluginRegistry::instance(); + ErasureCodeInterfaceRef erasure_code; + stringstream messages; + int code = instance.factory(plugin, + g_conf().get_val("erasure_code_dir"), + profile, &erasure_code, &messages); + if (code) { + cerr << messages.str() << endl; + return code; + } + + bufferlist in; + in.append(string(in_size, 'X')); + in.rebuild_aligned(ErasureCode::SIMD_ALIGN); + + 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; + + if (erased.size() > 0) { + for (vector::const_iterator i = erased.begin(); + i != erased.end(); + ++i) + encoded.erase(*i); + display_chunks(encoded, erasure_code->get_chunk_count()); + } + + utime_t begin_time = ceph_clock_now(); + for (int i = 0; i < max_iterations; i++) { + if (exhaustive_erasures) { + code = decode_erasures(encoded, encoded, 0, erasures, erasure_code); + if (code) + return code; + } else if (erased.size() > 0) { + map decoded; + code = erasure_code->decode(want_to_read, encoded, &decoded, 0); + if (code) + return code; + } else { + 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, 0); + if (code) + return code; + } + } + utime_t end_time = ceph_clock_now(); + cout << (end_time - begin_time) << "\t" << (max_iterations * (in_size / 1024)) << endl; + return 0; +} + +int main(int argc, char** argv) { + ErasureCodeBench ecbench; + try { + int err = ecbench.setup(argc, argv); + if (err) + return err; + return ecbench.run(); + } catch(po::error &e) { + cerr << e.what() << endl; + return 1; + } +} + +/* + * Local Variables: + * compile-command: "cd ../../../build ; make -j4 ceph_erasure_code_benchmark && + * valgrind --tool=memcheck --leak-check=full \ + * ./bin/ceph_erasure_code_benchmark \ + * --plugin jerasure \ + * --parameter directory=lib \ + * --parameter technique=reed_sol_van \ + * --parameter k=2 \ + * --parameter m=2 \ + * --iterations 1 + * " + * End: + */ -- cgit v1.2.3