2 * Copyright 2012 GRNET S.A. All rights reserved.
4 * Redistribution and use in source and binary forms, with or
5 * without modification, are permitted provided that the following
8 * 1. Redistributions of source code must retain the above
9 * copyright notice, this list of conditions and the following
11 * 2. Redistributions in binary form must reproduce the above
12 * copyright notice, this list of conditions and the following
13 * disclaimer in the documentation and/or other materials
14 * provided with the distribution.
16 * THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
17 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
20 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
23 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
24 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
26 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27 * POSSIBILITY OF SUCH DAMAGE.
29 * The views and conclusions contained in the software and
30 * documentation are those of the authors and should not be
31 * interpreted as representing official policies, either expressed
32 * or implied, of GRNET S.A.
39 #include <sys/syscall.h>
40 #include <sys/types.h>
42 #include <xseg/xseg.h>
47 #include <bench-xseg.h>
52 #define PRINT_SIG(__who, __sig) \
53 fprintf(stdout, "%s (%lu): id %lu, object %lu, offset %lu\n", \
54 #__who, (uint64_t)(__sig), \
55 ((struct signature *)__sig)->id, \
56 ((struct signature *)__sig)->object, \
57 ((struct signature *)__sig)->offset);
59 struct timespec delay = {0, 4000000};
61 /******************************\
62 * Static miscellaneous tools *
63 \******************************/
64 static inline uint64_t __get_id()
66 return atol(global_id + 6); /* cut the "bench-" part*/
69 static inline uint64_t __get_object_from_name(char *name)
71 return atol(name + IDLEN + 1); /* cut the "bench-908135-" part*/
74 static inline uint64_t __get_object(struct bench *prefs, uint64_t new)
77 new = new / (prefs->os / prefs->bs);
81 static inline int __snap_to_bound8(uint64_t space)
83 return space > 8 ? 8 : space;
86 static inline double __timespec2double(struct timespec num)
88 return (double) (num.tv_sec * pow(10, 9) + num.tv_nsec);
91 static inline void __write_sig(struct bench_lfsr *sg, uint64_t *d, uint64_t s,
98 /* Write random numbers (based on global_id) every 24 bytes */
99 /* TODO: Should we use memcpy? */
100 for (i = pos; i < (s / 8) - (3 - pos); i += 3)
101 *(d + i) = lfsr_next(sg);
103 /* special care for last chunk */
104 last_val = lfsr_next(sg);
105 space_left = s - (i * 8);
106 memcpy(d + i, &last_val, __snap_to_bound8(space_left));
109 static inline int __read_sig(struct bench_lfsr *sg, uint64_t *d, uint64_t s,
116 /* TODO: Should we use memcmp? */
117 for (i = pos; i < (s / 8) - (3 - pos); i += 3) {
118 if (*(d + i) != lfsr_next(sg))
121 /* special care for last chunk */
122 last_val = lfsr_next(sg);
123 space_left = s - (i * 8);
124 if (memcmp(d + i, &last_val, __snap_to_bound8(space_left)))
131 * Seperates a double number in seconds, msec, usec, nsec
132 * Expects a number in nanoseconds (e.g. a number from timespec2double)
134 static struct tm_result __separate_by_order(double num)
136 struct tm_result res;
138 //The format we expect is the following:
140 // |-s-|-ms-|-us-|-ns|
141 //num = 123 456 789 012 . 000000000000
142 res.s = num / pow(10,9);
143 num = fmod(num, pow(10,9));
144 res.ms = num / pow(10,6);
145 num = fmod(num, pow(10,6));
147 res.ns = fmod(num, 1000);
152 static void __calculate_bw(struct bench *prefs, double iops, struct bw *bw)
154 bw->val = iops * prefs->bs;
155 strcpy(bw->unit, "B/s");
160 bw->val = bw->val / 1024;
161 strcpy(bw->unit, "KB/s");
166 bw->val = bw->val / 1024;
167 strcpy(bw->unit, "MB/s");
172 bw->val = bw->val / 1024;
173 strcpy(bw->unit, "GB/s");
176 static double __calculate_iops(struct bench *prefs, double elapsed_ns)
178 /* elapsed_ns is in nanoseconds, so we convert it to seconds */
179 double elapsed = elapsed_ns / pow(10,9);
180 return (prefs->status->received / elapsed);
183 /******************************\
184 * Argument-parsing functions *
185 \******************************/
188 * Convert string to size in bytes.
189 * If syntax is invalid, return 0. Values such as zero and non-integer
190 * multiples of segment's page size should not be accepted.
192 uint64_t str2num(char *str)
197 num = strtoll(str, &unit, 10);
198 if (strlen(unit) > 1) //Invalid syntax
200 else if (strlen(unit) < 1) //Plain number in bytes
221 * Converts struct timespec to double (units in nanoseconds)
223 int read_insanity(char *insanity)
225 if (strncmp(insanity, "sane", MAX_ARG_LEN + 1) == 0)
226 return INSANITY_SANE;
227 if (strncmp(insanity, "eccentric", MAX_ARG_LEN + 1) == 0)
228 return INSANITY_ECCENTRIC;
229 if (strncmp(insanity, "manic", MAX_ARG_LEN + 1) == 0)
230 return INSANITY_MANIC;
231 if (strncmp(insanity, "paranoid", MAX_ARG_LEN + 1) == 0)
232 return INSANITY_PARANOID;
236 int read_op(char *op)
238 if (strncmp(op, "read", MAX_ARG_LEN + 1) == 0)
240 if (strncmp(op, "write", MAX_ARG_LEN + 1) == 0)
242 if (strncmp(op, "info", MAX_ARG_LEN + 1) == 0)
244 if (strncmp(op, "delete", MAX_ARG_LEN + 1) == 0)
249 int read_verify(char *verify)
251 if (strncmp(verify, "no", MAX_ARG_LEN + 1) == 0)
253 if (strncmp(verify, "meta", MAX_ARG_LEN + 1) == 0)
255 if (strncmp(verify, "full", MAX_ARG_LEN + 1) == 0)
260 int read_progress(char *progress)
262 if (strncmp(progress, "no", MAX_ARG_LEN + 1) == 0)
264 if (strncmp(progress, "yes", MAX_ARG_LEN + 1) == 0)
269 int read_ping(char *ping)
271 if (strncmp(ping, "no", MAX_ARG_LEN + 1) == 0)
272 return PING_MODE_OFF;
273 if (strncmp(ping, "yes", MAX_ARG_LEN + 1) == 0)
278 int read_pattern(char *pattern)
280 if (strncmp(pattern, "seq", MAX_ARG_LEN + 1) == 0)
282 if (strncmp(pattern, "rand", MAX_ARG_LEN + 1) == 0)
287 /*******************\
289 \*******************/
291 void print_io_stats(struct bench *prefs, double elapsed)
297 * We could malloc struct bw in __calculate_bw, but it's safer in cases when
298 * there is no memory left.
300 iops = __calculate_iops(prefs, elapsed);
301 __calculate_bw(prefs, iops, &bw);
303 fprintf(stdout, " ~~~~~~~~~~~~~~~~~~~~~~~~\n");
304 if (prefs->op == X_READ || prefs->op == X_WRITE)
305 fprintf(stdout, "Bandwidth: %.3lf %s\n", bw.val, bw.unit);
306 fprintf(stdout, "IOPS: %.3lf\n", iops);
309 void print_stats(struct bench *prefs)
312 "Requests total: %10lu\n"
313 "Requests submitted: %10lu\n"
314 "Requests received: %10lu\n"
315 "Requests failed: %10lu\n",
317 prefs->status->submitted,
318 prefs->status->received,
319 prefs->status->failed);
320 if ((prefs->op == X_READ) && (GET_FLAG(VERIFY, prefs->flags) != VERIFY_NO))
321 fprintf(stdout, "Requests corrupted: %10lu\n", prefs->status->corrupted);
322 fprintf(stdout, "\n");
326 void print_remaining(struct bench *prefs)
330 remaining = prefs->status->max - prefs->status->received;
332 fprintf(stdout, "Requests remaining: %10lu\n", remaining);
334 fprintf(stdout, "All requests have been served.\n");
338 void print_res(struct bench *prefs)
341 struct tm_result res, res_rec;
345 tm = prefs->total_tm;
346 sum = __timespec2double(tm->sum);
347 res = __separate_by_order(sum);
349 fprintf(stdout, "\n");
350 fprintf(stdout, " Benchmark results\n");
351 fprintf(stdout, " ========================\n");
352 fprintf(stdout, " |-s-||-ms-|-us-|-ns-|\n");
353 fprintf(stdout, "Total time: %3u. %03u %03u %03u\n",
354 res.s, res.ms, res.us, res.ns);
356 if (!prefs->status->received) {
362 if (GET_FLAG(INSANITY, prefs->flags) < tm->insanity)
365 sum_rec = __timespec2double(tm->sum);
366 res_rec = __separate_by_order(sum_rec / prefs->status->received);
368 fprintf(stdout, "Avg. latency: %3u. %03u %03u %03u\n",
369 res_rec.s, res_rec.ms, res_rec.us, res_rec.ns);
372 print_io_stats(prefs, sum);
376 void print_progress(struct bench *prefs)
380 if ((prefs->op == X_READ) && (GET_FLAG(VERIFY, prefs->flags) != VERIFY_NO))
383 fprintf(stdout, "\033[%dA\033[J", lines);
387 /**************************\
388 * Benchmarking functions *
389 \**************************/
391 void create_id(unsigned long seed)
393 if (seed >= pow(10, 9))
394 XSEGLOG2(&lc, W, "Seed larger than 10^9, only its first 9 digits will "
397 //nanoseconds can't be more than 9 digits
398 snprintf(global_id, IDLEN + 1, "bench-%09lu", seed);
401 void create_target(struct bench *prefs, struct xseg_request *req,
404 struct xseg *xseg = prefs->peer->xseg;
406 char buf[TARGETLEN + 1];
408 req_target = xseg_get_target(xseg, req);
410 //For read/write, the target object may not correspond to `new`, which is
411 //actually the chunk number.
412 new = __get_object(prefs, new);
413 snprintf(buf, TARGETLEN + 1, "%s-%016lu", global_id, new);
414 strncpy(req_target, buf, TARGETLEN);
415 XSEGLOG2(&lc, D, "Target name of request is %s\n", buf);
419 uint64_t determine_next(struct bench *prefs)
421 if (GET_FLAG(PATTERN, prefs->flags) == PATTERN_SEQ)
422 return prefs->status->submitted;
424 return lfsr_next(prefs->lfsr);
427 uint64_t calculate_offset(struct bench *prefs, uint64_t new)
430 return (new * prefs->bs) % prefs->os;
435 uint64_t calculate_prog_quantum(struct bench *prefs)
437 return round((double)prefs->status->max / 20.0);
442 * ***********************************************
443 * `create_chunk` handles 3 identifiers:
444 * 1. The benchmark's global_id
445 * 2. The object's number
446 * 3. The chunk offset in the object
448 * ************************************************
449 * `readwrite_chunk_full` takes the above 3 identifiers and feeds them as seeds
450 * in 63-bit LFSRs. The numbers generated are written consecutively in chunk's
451 * memory range. For example, for a 72-byte chunk:
453 * || 1 | 2 | 3 | 1 | 2 | 3 | 1 | 2 | 3 ||
454 * ^ 8 16 24 32 40 48 56 64 ^
459 * 1,2,3 differ between each iteration
461 * **************************************************
462 * `_create_chunk_meta` simply writes the above 3 ids in the start and end of
463 * the chunk's memory range, so it should be much faster (but less safe)
465 * **************************************************
466 * In both cases, special care is taken not to exceed the chunk's memory range.
467 * Also, the bare minimum chunk to verify should be 48 bytes. This limit is set
468 * by reeadwrite_chunk_meta, which expects to write in a memory at least this
471 * **************************************************
472 * Note: The diagram above also represents the x86_64's endianness.
473 * Endianness must be taken into careful consideration when examining a memory
476 static int readwrite_chunk_full(struct xseg *xseg, struct xseg_request *req,
477 uint64_t id, uint64_t object)
479 struct bench_lfsr id_lfsr;
480 struct bench_lfsr obj_lfsr;
481 struct bench_lfsr off_lfsr;
482 uint64_t *d = (uint64_t *)xseg_get_data(xseg, req);
483 uint64_t s = req->size;
485 /* Create 63-bit LFSRs */
486 lfsr_init(&id_lfsr, 0x7FFFFFFFFFFFFFFF, id, 0);
487 lfsr_init(&obj_lfsr, 0x7FFFFFFFFFFFFFFF, object, 0);
488 lfsr_init(&off_lfsr, 0x7FFFFFFFFFFFFFFF, req->offset, 0);
490 if (s < sizeof(struct signature)) {
491 XSEGLOG2(&lc, E, "Too small chunk size (%lu butes). Leaving.", s);
496 * Every write operation has its read counterpart which, if it finds any
497 * corruption, returns 1
500 if (req->op == X_WRITE) {
501 __write_sig(&id_lfsr, d, s, 0);
502 __write_sig(&obj_lfsr, d, s, 1);
503 __write_sig(&off_lfsr, d, s, 2);
505 if (__read_sig(&id_lfsr, d, s, 0))
507 if (__read_sig(&obj_lfsr, d, s, 1))
509 if(__read_sig(&off_lfsr, d, s, 2))
516 static int readwrite_chunk_meta(struct xseg *xseg, struct xseg_request *req,
517 uint64_t id, uint64_t object)
519 char *d = xseg_get_data(xseg, req);
520 uint64_t s = req->size;
521 struct signature sig;
522 int sig_s = sizeof(struct signature);
527 sig.offset = req->offset;
530 XSEGLOG2(&lc, E, "Too small chunk size (%lu butes). Leaving.", s);
534 //PRINT_SIG(expected, (&sig));
535 /* Read/Write chunk signature both at its start and at its end */
536 if (req->op == X_WRITE) {
537 memcpy(d, &sig, sig_s);
538 memcpy(d + s - sig_s, &sig, sig_s);
540 if (memcmp(d, &sig, sig_s))
542 else if (memcmp(d + s - sig_s, &sig, sig_s))
545 //PRINT_SIG(start, d);
546 //PRINT_SIG(end, (d + s - sig_s));
551 * We want these functions to be as fast as possible in case we haven't asked
553 * TODO: Make them prettier but keep the speed of this implementation
555 void create_chunk(struct bench *prefs, struct xseg_request *req, uint64_t new)
557 struct xseg *xseg = prefs->peer->xseg;
562 verify = GET_FLAG(VERIFY, prefs->flags);
568 object = __get_object(prefs, new);
569 readwrite_chunk_meta(xseg, req, id, object);
573 object = __get_object(prefs, new);
574 readwrite_chunk_full(xseg, req, id, object);
577 XSEGLOG2(&lc, W, "Unexpected verification mode: %d\n", verify);
581 int read_chunk(struct bench *prefs, struct xseg_request *req)
583 struct xseg *xseg = prefs->peer->xseg;
590 verify = GET_FLAG(VERIFY, prefs->flags);
596 target = xseg_get_target(xseg, req);
597 object = __get_object_from_name(target);
598 r = readwrite_chunk_meta(xseg, req, id, object);
602 target = xseg_get_target(xseg, req);
603 object = __get_object_from_name(target);
604 r = readwrite_chunk_full(xseg, req, id, object);
607 XSEGLOG2(&lc, W, "Unexpected verification mode: %d\n", verify);