// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- #include #include #include #include #include #include #include #include #include #include #include #include #include "common/Formatter.h" #include "bencher.h" #include "rados_backend.h" #include "detailed_stat_collector.h" #include "distribution.h" #include "global/global_init.h" #include "os/filestore/FileStore.h" #include "testfilestore_backend.h" #include "common/perf_counters.h" namespace po = boost::program_options; using namespace std; struct MorePrinting : public DetailedStatCollector::AdditionalPrinting { CephContext *cct; explicit MorePrinting(CephContext *cct) : cct(cct) {} void operator()(std::ostream *out) override { bufferlist bl; Formatter *f = Formatter::create("json-pretty"); cct->get_perfcounters_collection()->dump_formatted(f, 0); f->flush(bl); delete f; bl.append('\0'); *out << bl.c_str() << std::endl; } }; int main(int argc, char **argv) { po::options_description desc("Allowed options"); desc.add_options() ("help", "produce help message") ("num-concurrent-ops", po::value()->default_value(10), "set number of concurrent ops") ("num-objects", po::value()->default_value(500), "set number of objects to use") ("object-size", po::value()->default_value(4<<20), "set object size") ("io-size", po::value()->default_value(4<<10), "set io size") ("write-ratio", po::value()->default_value(0.75), "set ratio of read to write") ("duration", po::value()->default_value(0), "set max duration, 0 for unlimited") ("max-ops", po::value()->default_value(0), "set max ops, 0 for unlimited") ("seed", po::value(), "seed") ("num-colls", po::value()->default_value(20), "number of collections") ("op-dump-file", po::value()->default_value(""), "set file for dumping op details, omit for stderr") ("filestore-path", po::value(), "path to filestore directory, mandatory") ("journal-path", po::value(), "path to journal, mandatory") ("offset-align", po::value()->default_value(4096), "align offset by") ("write-infos", po::value()->default_value(false), "write info objects with main writes") ("sequential", po::value()->default_value(false), "do sequential writes like rbd") ("disable-detailed-ops", po::value()->default_value(false), "don't dump per op stats") ("num-writers", po::value()->default_value(1), "num write threads") ; vector ceph_option_strings; po::variables_map vm; try { po::parsed_options parsed = po::command_line_parser(argc, argv).options(desc).allow_unregistered().run(); po::store( parsed, vm); po::notify(vm); ceph_option_strings = po::collect_unrecognized(parsed.options, po::include_positional); } catch(po::error &e) { std::cerr << e.what() << std::endl; return 1; } vector ceph_options, def_args; 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()); } auto cct = 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("filestore-path") || !vm.count("journal-path")) { cout << "Must provide filestore-path and journal-path" << std::endl << desc << std::endl; return 1; } if (vm.count("help")) { cout << desc << std::endl; return 1; } rngen_t rng; if (vm.count("seed")) rng = rngen_t(vm["seed"].as()); set > ops; ops.insert(make_pair(vm["write-ratio"].as(), Bencher::WRITE)); ops.insert(make_pair(1-vm["write-ratio"].as(), Bencher::READ)); FileStore fs(g_ceph_context, vm["filestore-path"].as(), vm["journal-path"].as()); ObjectStore::Sequencer osr(__func__); if (fs.mkfs() < 0) { cout << "mkfs failed" << std::endl; return 1; } if (fs.mount() < 0) { cout << "mount failed" << std::endl; return 1; } ostream *detailed_ops = 0; ofstream myfile; if (vm["disable-detailed-ops"].as()) { detailed_ops = 0; } else if (vm["op-dump-file"].as().size()) { myfile.open(vm["op-dump-file"].as().c_str()); detailed_ops = &myfile; } else { detailed_ops = &cerr; } ceph::shared_ptr col( new DetailedStatCollector( 1, new JSONFormatter, detailed_ops, &cout, new MorePrinting(g_ceph_context))); cout << "Creating objects.." << std::endl; bufferlist bl; for (uint64_t i = 0; i < vm["object-size"].as(); ++i) { bl.append(0); } for (uint64_t num = 0; num < vm["num-colls"].as(); ++num) { spg_t pgid(pg_t(num, 0), shard_id_t::NO_SHARD); std::cout << "collection " << pgid << std::endl; ObjectStore::Transaction t; t.create_collection(coll_t(pgid), 0); fs.apply_transaction(&osr, std::move(t)); } { ObjectStore::Transaction t; t.create_collection(coll_t(), 0); fs.apply_transaction(&osr, std::move(t)); } vector > benchers( vm["num-writers"].as()); for (vector >::iterator i = benchers.begin(); i != benchers.end(); ++i) { set objects; for (uint64_t num = 0; num < vm["num-objects"].as(); ++num) { unsigned col_num = num % vm["num-colls"].as(); spg_t pgid(pg_t(col_num, 0), shard_id_t::NO_SHARD); stringstream obj; obj << "obj_" << num << "_bencher_" << (i - benchers.begin()); objects.insert(coll_t(pgid).to_str() + string("/") + obj.str()); } Distribution< boost::tuple > *gen = 0; if (vm["sequential"].as()) { std::cout << "Using Sequential generator" << std::endl; gen = new SequentialLoad( objects, vm["object-size"].as(), vm["io-size"].as(), new WeightedDist(rng, ops) ); } else { std::cout << "Using random generator" << std::endl; gen = new FourTupleDist( new RandomDist(rng, objects), new Align( new UniformRandom( rng, 0, vm["object-size"].as() - vm["io-size"].as()), vm["offset-align"].as() ), new Uniform(vm["io-size"].as()), new WeightedDist(rng, ops) ); } Bencher *bencher = new Bencher( gen, col, new TestFileStoreBackend(&fs, vm["write-infos"].as()), vm["num-concurrent-ops"].as(), vm["duration"].as(), vm["max-ops"].as()); bencher->init(objects, vm["object-size"].as(), &std::cout); cout << "Created objects..." << std::endl; (*i).reset(bencher); } for (vector >::iterator i = benchers.begin(); i != benchers.end(); ++i) { (*i)->create("bencher"); } for (vector >::iterator i = benchers.begin(); i != benchers.end(); ++i) { (*i)->join(); } fs.umount(); if (vm["op-dump-file"].as().size()) { myfile.close(); } return 0; }