]> git.proxmox.com Git - ceph.git/blame - ceph/src/test/erasure-code/ceph_erasure_code_benchmark.cc
update ceph source to reef 18.1.2
[ceph.git] / ceph / src / test / erasure-code / ceph_erasure_code_benchmark.cc
CommitLineData
7c673cae
FG
1// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2// vim: ts=8 sw=2 smarttab
3/*
4 * Ceph distributed storage system
5 *
6 * Copyright (C) 2013,2014 Cloudwatt <libre.licensing@cloudwatt.com>
7 * Copyright (C) 2014 Red Hat <contact@redhat.com>
8 *
9 * Author: Loic Dachary <loic@dachary.org>
10 *
11 * This library is free software; you can redistribute it and/or
12 * modify it under the terms of the GNU Lesser General Public
13 * License as published by the Free Software Foundation; either
14 * version 2.1 of the License, or (at your option) any later version.
15 *
16 */
17
18#include <boost/scoped_ptr.hpp>
19#include <boost/lexical_cast.hpp>
20#include <boost/program_options/option.hpp>
21#include <boost/program_options/options_description.hpp>
22#include <boost/program_options/variables_map.hpp>
23#include <boost/program_options/cmdline.hpp>
24#include <boost/program_options/parsers.hpp>
25#include <boost/algorithm/string.hpp>
26
27#include "global/global_context.h"
28#include "global/global_init.h"
29#include "common/ceph_argparse.h"
11fdf7f2 30#include "common/ceph_context.h"
7c673cae
FG
31#include "common/config.h"
32#include "common/Clock.h"
33#include "include/utime.h"
34#include "erasure-code/ErasureCodePlugin.h"
35#include "erasure-code/ErasureCode.h"
36#include "ceph_erasure_code_benchmark.h"
37
1e59de90
TL
38using std::endl;
39using std::cerr;
40using std::cout;
41using std::map;
42using std::set;
43using std::string;
44using std::stringstream;
45using std::vector;
46
7c673cae
FG
47namespace po = boost::program_options;
48
49int ErasureCodeBench::setup(int argc, char** argv) {
50
51 po::options_description desc("Allowed options");
52 desc.add_options()
53 ("help,h", "produce help message")
54 ("verbose,v", "explain what happens")
55 ("size,s", po::value<int>()->default_value(1024 * 1024),
56 "size of the buffer to be encoded")
57 ("iterations,i", po::value<int>()->default_value(1),
58 "number of encode/decode runs")
59 ("plugin,p", po::value<string>()->default_value("jerasure"),
60 "erasure code plugin name")
61 ("workload,w", po::value<string>()->default_value("encode"),
62 "run either encode or decode")
63 ("erasures,e", po::value<int>()->default_value(1),
64 "number of erasures when decoding")
65 ("erased", po::value<vector<int> >(),
66 "erased chunk (repeat if more than one chunk is erased)")
67 ("erasures-generation,E", po::value<string>()->default_value("random"),
68 "If set to 'random', pick the number of chunks to recover (as specified by "
69 " --erasures) at random. If set to 'exhaustive' try all combinations of erasures "
70 " (i.e. k=4,m=3 with one erasure will try to recover from the erasure of "
71 " the first chunk, then the second etc.)")
72 ("parameter,P", po::value<vector<string> >(),
73 "add a parameter to the erasure code profile")
74 ;
75
76 po::variables_map vm;
77 po::parsed_options parsed =
78 po::command_line_parser(argc, argv).options(desc).allow_unregistered().run();
79 po::store(
80 parsed,
81 vm);
82 po::notify(vm);
83
11fdf7f2 84 vector<const char *> ceph_options;
7c673cae
FG
85 vector<string> ceph_option_strings = po::collect_unrecognized(
86 parsed.options, po::include_positional);
87 ceph_options.reserve(ceph_option_strings.size());
88 for (vector<string>::iterator i = ceph_option_strings.begin();
89 i != ceph_option_strings.end();
90 ++i) {
91 ceph_options.push_back(i->c_str());
92 }
93
94 cct = global_init(
11fdf7f2 95 NULL, ceph_options, CEPH_ENTITY_TYPE_CLIENT,
7c673cae
FG
96 CODE_ENVIRONMENT_UTILITY,
97 CINIT_FLAG_NO_DEFAULT_CONFIG_FILE);
98 common_init_finish(g_ceph_context);
11fdf7f2 99 g_ceph_context->_conf.apply_changes(nullptr);
7c673cae
FG
100
101 if (vm.count("help")) {
102 cout << desc << std::endl;
103 return 1;
104 }
105
106 if (vm.count("parameter")) {
107 const vector<string> &p = vm["parameter"].as< vector<string> >();
108 for (vector<string>::const_iterator i = p.begin();
109 i != p.end();
110 ++i) {
111 std::vector<std::string> strs;
112 boost::split(strs, *i, boost::is_any_of("="));
113 if (strs.size() != 2) {
114 cerr << "--parameter " << *i << " ignored because it does not contain exactly one =" << endl;
115 } else {
116 profile[strs[0]] = strs[1];
117 }
118 }
119 }
120
121 in_size = vm["size"].as<int>();
122 max_iterations = vm["iterations"].as<int>();
123 plugin = vm["plugin"].as<string>();
124 workload = vm["workload"].as<string>();
125 erasures = vm["erasures"].as<int>();
126 if (vm.count("erasures-generation") > 0 &&
127 vm["erasures-generation"].as<string>() == "exhaustive")
128 exhaustive_erasures = true;
129 else
130 exhaustive_erasures = false;
131 if (vm.count("erased") > 0)
132 erased = vm["erased"].as<vector<int> >();
7c673cae 133
9f95a23c
TL
134 try {
135 k = stoi(profile["k"]);
136 m = stoi(profile["m"]);
137 } catch (const std::logic_error& e) {
138 cout << "Invalid k and/or m: k=" << profile["k"] << ", m=" << profile["m"]
139 << " (" << e.what() << ")" << endl;
140 return -EINVAL;
141 }
7c673cae
FG
142 if (k <= 0) {
143 cout << "parameter k is " << k << ". But k needs to be > 0." << endl;
144 return -EINVAL;
145 } else if ( m < 0 ) {
146 cout << "parameter m is " << m << ". But m needs to be >= 0." << endl;
147 return -EINVAL;
148 }
149
150 verbose = vm.count("verbose") > 0 ? true : false;
151
152 return 0;
153}
154
155int ErasureCodeBench::run() {
156 ErasureCodePluginRegistry &instance = ErasureCodePluginRegistry::instance();
157 instance.disable_dlclose = true;
158
159 if (workload == "encode")
160 return encode();
161 else
162 return decode();
163}
164
165int ErasureCodeBench::encode()
166{
167 ErasureCodePluginRegistry &instance = ErasureCodePluginRegistry::instance();
168 ErasureCodeInterfaceRef erasure_code;
169 stringstream messages;
170 int code = instance.factory(plugin,
11fdf7f2 171 g_conf().get_val<std::string>("erasure_code_dir"),
7c673cae
FG
172 profile, &erasure_code, &messages);
173 if (code) {
174 cerr << messages.str() << endl;
175 return code;
176 }
177
7c673cae
FG
178 bufferlist in;
179 in.append(string(in_size, 'X'));
180 in.rebuild_aligned(ErasureCode::SIMD_ALIGN);
181 set<int> want_to_encode;
182 for (int i = 0; i < k + m; i++) {
183 want_to_encode.insert(i);
184 }
185 utime_t begin_time = ceph_clock_now();
186 for (int i = 0; i < max_iterations; i++) {
1e59de90 187 std::map<int,bufferlist> encoded;
7c673cae
FG
188 code = erasure_code->encode(want_to_encode, in, &encoded);
189 if (code)
190 return code;
191 }
192 utime_t end_time = ceph_clock_now();
193 cout << (end_time - begin_time) << "\t" << (max_iterations * (in_size / 1024)) << endl;
194 return 0;
195}
196
197static void display_chunks(const map<int,bufferlist> &chunks,
198 unsigned int chunk_count) {
199 cout << "chunks ";
200 for (unsigned int chunk = 0; chunk < chunk_count; chunk++) {
201 if (chunks.count(chunk) == 0) {
202 cout << "(" << chunk << ")";
203 } else {
204 cout << " " << chunk << " ";
205 }
206 cout << " ";
207 }
208 cout << "(X) is an erased chunk" << endl;
209}
210
211int ErasureCodeBench::decode_erasures(const map<int,bufferlist> &all_chunks,
212 const map<int,bufferlist> &chunks,
213 unsigned i,
214 unsigned want_erasures,
215 ErasureCodeInterfaceRef erasure_code)
216{
217 int code = 0;
218
219 if (want_erasures == 0) {
220 if (verbose)
221 display_chunks(chunks, erasure_code->get_chunk_count());
222 set<int> want_to_read;
223 for (unsigned int chunk = 0; chunk < erasure_code->get_chunk_count(); chunk++)
224 if (chunks.count(chunk) == 0)
225 want_to_read.insert(chunk);
226
227 map<int,bufferlist> decoded;
11fdf7f2 228 code = erasure_code->decode(want_to_read, chunks, &decoded, 0);
7c673cae
FG
229 if (code)
230 return code;
231 for (set<int>::iterator chunk = want_to_read.begin();
232 chunk != want_to_read.end();
233 ++chunk) {
234 if (all_chunks.find(*chunk)->second.length() != decoded[*chunk].length()) {
235 cerr << "chunk " << *chunk << " length=" << all_chunks.find(*chunk)->second.length()
236 << " decoded with length=" << decoded[*chunk].length() << endl;
237 return -1;
238 }
239 bufferlist tmp = all_chunks.find(*chunk)->second;
240 if (!tmp.contents_equal(decoded[*chunk])) {
241 cerr << "chunk " << *chunk
242 << " content and recovered content are different" << endl;
243 return -1;
244 }
245 }
246 return 0;
247 }
248
249 for (; i < erasure_code->get_chunk_count(); i++) {
250 map<int,bufferlist> one_less = chunks;
251 one_less.erase(i);
252 code = decode_erasures(all_chunks, one_less, i + 1, want_erasures - 1, erasure_code);
253 if (code)
254 return code;
255 }
256
257 return 0;
258}
259
260int ErasureCodeBench::decode()
261{
262 ErasureCodePluginRegistry &instance = ErasureCodePluginRegistry::instance();
263 ErasureCodeInterfaceRef erasure_code;
264 stringstream messages;
265 int code = instance.factory(plugin,
11fdf7f2 266 g_conf().get_val<std::string>("erasure_code_dir"),
7c673cae
FG
267 profile, &erasure_code, &messages);
268 if (code) {
269 cerr << messages.str() << endl;
270 return code;
271 }
11fdf7f2 272
7c673cae
FG
273 bufferlist in;
274 in.append(string(in_size, 'X'));
275 in.rebuild_aligned(ErasureCode::SIMD_ALIGN);
276
277 set<int> want_to_encode;
278 for (int i = 0; i < k + m; i++) {
279 want_to_encode.insert(i);
280 }
281
282 map<int,bufferlist> encoded;
283 code = erasure_code->encode(want_to_encode, in, &encoded);
284 if (code)
285 return code;
286
287 set<int> want_to_read = want_to_encode;
288
289 if (erased.size() > 0) {
290 for (vector<int>::const_iterator i = erased.begin();
291 i != erased.end();
292 ++i)
293 encoded.erase(*i);
294 display_chunks(encoded, erasure_code->get_chunk_count());
295 }
296
297 utime_t begin_time = ceph_clock_now();
298 for (int i = 0; i < max_iterations; i++) {
299 if (exhaustive_erasures) {
300 code = decode_erasures(encoded, encoded, 0, erasures, erasure_code);
301 if (code)
302 return code;
303 } else if (erased.size() > 0) {
304 map<int,bufferlist> decoded;
11fdf7f2 305 code = erasure_code->decode(want_to_read, encoded, &decoded, 0);
7c673cae
FG
306 if (code)
307 return code;
308 } else {
309 map<int,bufferlist> chunks = encoded;
310 for (int j = 0; j < erasures; j++) {
311 int erasure;
312 do {
313 erasure = rand() % ( k + m );
314 } while(chunks.count(erasure) == 0);
315 chunks.erase(erasure);
316 }
317 map<int,bufferlist> decoded;
11fdf7f2 318 code = erasure_code->decode(want_to_read, chunks, &decoded, 0);
7c673cae
FG
319 if (code)
320 return code;
321 }
322 }
323 utime_t end_time = ceph_clock_now();
324 cout << (end_time - begin_time) << "\t" << (max_iterations * (in_size / 1024)) << endl;
325 return 0;
326}
327
328int main(int argc, char** argv) {
329 ErasureCodeBench ecbench;
330 try {
331 int err = ecbench.setup(argc, argv);
332 if (err)
333 return err;
334 return ecbench.run();
335 } catch(po::error &e) {
336 cerr << e.what() << endl;
337 return 1;
338 }
339}
340
341/*
342 * Local Variables:
11fdf7f2 343 * compile-command: "cd ../../../build ; make -j4 ceph_erasure_code_benchmark &&
7c673cae 344 * valgrind --tool=memcheck --leak-check=full \
11fdf7f2 345 * ./bin/ceph_erasure_code_benchmark \
7c673cae 346 * --plugin jerasure \
11fdf7f2 347 * --parameter directory=lib \
7c673cae
FG
348 * --parameter technique=reed_sol_van \
349 * --parameter k=2 \
350 * --parameter m=2 \
351 * --iterations 1
352 * "
353 * End:
354 */