Statistics
| Branch: | Tag: | Revision:

root / xseg / peers / user / bench-utils.c @ 7e21df56

History | View | Annotate | Download (14.8 kB)

1
/*
2
 * Copyright 2012 GRNET S.A. All rights reserved.
3
 *
4
 * Redistribution and use in source and binary forms, with or
5
 * without modification, are permitted provided that the following
6
 * conditions are met:
7
 *
8
 *   1. Redistributions of source code must retain the above
9
 *      copyright notice, this list of conditions and the following
10
 *      disclaimer.
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.
15
 *
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.
28
 *
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.
33
 */
34

    
35
#define _GNU_SOURCE
36
#include <stdio.h>
37
#include <stdlib.h>
38
#include <unistd.h>
39
#include <sys/syscall.h>
40
#include <sys/types.h>
41
#include <pthread.h>
42
#include <xseg/xseg.h>
43
#include <peer.h>
44
#include <time.h>
45
#include <sys/util.h>
46
#include <signal.h>
47
#include <bench-xseg.h>
48

    
49
#include <math.h>
50
#include <string.h>
51

    
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);
58

    
59
struct timespec delay = {0, 4000000};
60

    
61
/******************************\
62
 * Static miscellaneous tools *
63
\******************************/
64
static inline uint64_t __get_id()
65
{
66
        return atol(global_id + 6); /* cut the "bench-" part*/
67
}
68

    
69
static inline uint64_t __get_object_from_name(char *name)
70
{
71
        return atol(name + IDLEN); /* cut the "bench-908135-" part*/
72
}
73

    
74
static inline uint64_t __get_object(struct bench *prefs, uint64_t new)
75
{
76
        if (prefs->ts > 0)
77
                new = new / (prefs->os / prefs->bs);
78
        return new;
79
}
80

    
81
static inline int __snap_to_bound8(uint64_t space)
82
{
83
        return space > 8 ? 8 : space;
84
}
85

    
86
static inline double __timespec2double(struct timespec num)
87
{
88
        return (double) (num.tv_sec * pow(10, 9) + num.tv_nsec);
89
}
90

    
91
static inline void __write_sig(struct bench_lfsr *sg,        uint64_t *d, uint64_t s,
92
                int pos)
93
{
94
        uint64_t i;
95
        uint64_t last_val;
96
        uint64_t space_left;
97

    
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);
102

    
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));
107
}
108

    
109
static inline int __read_sig(struct bench_lfsr *sg, uint64_t *d, uint64_t s,
110
                int pos)
111
{
112
        uint64_t i;
113
        uint64_t last_val;
114
        uint64_t space_left;
115

    
116
        /* TODO: Should we use memcmp? */
117
        for (i = pos; i < (s / 8) - (3 - pos); i += 3) {
118
                if (*(d + i) != lfsr_next(sg))
119
                        return 1;
120
        }
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)))
125
                return 1;
126

    
127
        return 0;
128
}
129

    
130
/*
131
 * Seperates a double number in seconds, msec, usec, nsec
132
 * Expects a number in nanoseconds (e.g. a number from timespec2double)
133
 */
134
static struct tm_result __separate_by_order(double num)
135
{
136
        struct tm_result res;
137

    
138
        //The format we expect is the following:
139
        //
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));
146
        res.us = num / 1000;
147
        res.ns = fmod(num, 1000);
148

    
149
        return res;
150
}
151

    
152
static void __calculate_bw(struct bench *prefs, double iops, struct bw *bw)
153
{
154
        bw->val = iops * prefs->bs;
155
        strcpy(bw->unit, "B/s");
156

    
157
        if (bw->val < 1024)
158
                return;
159

    
160
        bw->val = bw->val / 1024;
161
        strcpy(bw->unit, "KB/s");
162

    
163
        if (bw->val < 1024)
164
                return;
165

    
166
        bw->val = bw->val / 1024;
167
        strcpy(bw->unit, "MB/s");
168

    
169
        if (bw->val < 1024)
170
                return;
171

    
172
        bw->val = bw->val / 1024;
173
        strcpy(bw->unit, "GB/s");
174
}
175

    
176
static double __calculate_iops(struct bench *prefs, double elapsed_ns)
177
{
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);
181
}
182

    
183
/******************************\
184
 * Argument-parsing functions *
185
\******************************/
186

    
187
/*
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.
191
 */
192
uint64_t str2num(char *str)
193
{
194
        char *unit;
195
        uint64_t num;
196

    
197
        num = strtoll(str, &unit, 10);
198
        if (strlen(unit) > 1) //Invalid syntax
199
                return 0;
200
        else if (strlen(unit) < 1) //Plain number in bytes
201
                return num;
202

    
203
        switch (*unit) {
204
                case 'g':
205
                case 'G':
206
                        num *= 1024;
207
                case 'm':
208
                case 'M':
209
                        num *= 1024;
210
                case 'k':
211
                case 'K':
212
                        num *= 1024;
213
                        break;
214
                default:
215
                        num = 0;
216
        }
217
        return num;
218
}
219

    
220
/*
221
 * Converts struct timespec to double (units in nanoseconds)
222
 */
223
int read_insanity(char *insanity)
224
{
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;
233
        return -1;
234
}
235

    
236
int read_op(char *op)
237
{
238
        if (strncmp(op, "read", MAX_ARG_LEN + 1) == 0)
239
                return X_READ;
240
        if (strncmp(op, "write", MAX_ARG_LEN + 1) == 0)
241
                return X_WRITE;
242
        if (strncmp(op, "info", MAX_ARG_LEN + 1) == 0)
243
                return X_INFO;
244
        if (strncmp(op, "delete", MAX_ARG_LEN + 1) == 0)
245
                return X_DELETE;
246
        return -1;
247
}
248

    
249
int read_verify(char *verify)
250
{
251
        if (strncmp(verify, "no", MAX_ARG_LEN + 1) == 0)
252
                return VERIFY_NO;
253
        if (strncmp(verify, "meta", MAX_ARG_LEN + 1) == 0)
254
                return VERIFY_META;
255
        if (strncmp(verify, "full", MAX_ARG_LEN + 1) == 0)
256
                return VERIFY_FULL;
257
        return -1;
258
}
259

    
260
int read_progress(char *progress)
261
{
262
        if (strncmp(progress, "no", MAX_ARG_LEN + 1) == 0)
263
                return PROGRESS_NO;
264
        if (strncmp(progress, "yes", MAX_ARG_LEN + 1) == 0)
265
                return PROGRESS_YES;
266
        return -1;
267
}
268

    
269
int read_pattern(char *pattern)
270
{
271
        if (strncmp(pattern, "seq", MAX_ARG_LEN + 1) == 0)
272
                return PATTERN_SEQ;
273
        if (strncmp(pattern, "rand", MAX_ARG_LEN + 1) == 0)
274
                return PATTERN_RAND;
275
        return -1;
276
}
277

    
278
/*******************\
279
 * Print functions *
280
\*******************/
281

    
282
void print_io_stats(struct bench *prefs, double elapsed)
283
{
284
        struct bw bw;
285
        double iops;
286

    
287
        /*
288
         * We could malloc struct bw in __calculate_bw, but it's safer in cases when
289
         * there is no memory left.
290
         */
291
        iops = __calculate_iops(prefs, elapsed);
292
        __calculate_bw(prefs, iops, &bw);
293

    
294
        fprintf(stdout, "           ~~~~~~~~~~~~~~~~~~~~~~~~\n");
295
        fprintf(stdout, "Bandwidth:    %.3lf %s\n", bw.val, bw.unit);
296
        fprintf(stdout, "IOPS:         %.3lf\n", iops);
297
        fflush(stdout);
298
}
299

    
300
void print_stats(struct bench *prefs)
301
{
302
        fprintf(stdout, "\n"
303
                        "Requests total:     %10lu\n"
304
                        "Requests submitted: %10lu\n"
305
                        "Requests received:  %10lu\n"
306
                        "Requests failed:    %10lu\n",
307
                        prefs->status->max,
308
                        prefs->status->submitted,
309
                        prefs->status->received,
310
                        prefs->status->failed);
311
        if ((prefs->op == X_READ) && (GET_FLAG(VERIFY, prefs->flags) != VERIFY_NO))
312
                fprintf(stdout, "Requests corrupted: %10lu\n", prefs->status->corrupted);
313
        fprintf(stdout, "\n");
314
        fflush(stdout);
315
}
316

    
317
void print_remaining(struct bench *prefs)
318
{
319
        uint64_t remaining;
320

    
321
        remaining = prefs->status->max - prefs->status->received;
322
        if (remaining)
323
                fprintf(stdout, "Requests remaining: %10lu\n", remaining);
324
        else
325
                fprintf(stdout, "All requests have been served.\n");
326
        fflush(stdout);
327
}
328

    
329
void print_res(struct bench *prefs, struct timer *tm, char *type)
330
{
331
        struct tm_result res;
332
        double sum;
333

    
334
        sum = __timespec2double(tm->sum);
335
        res = __separate_by_order(sum);
336

    
337
        fprintf(stdout, "\n");
338
        fprintf(stdout, "              %s\n", type);
339
        fprintf(stdout, "           ========================\n");
340
        fprintf(stdout, "             |-s-||-ms-|-us-|-ns-|\n");
341
        fprintf(stdout, "Total time:   %3u. %03u  %03u  %03u\n",
342
                        res.s, res.ms, res.us, res.ns);
343
        fflush(stdout);
344

    
345
        if (!prefs->status->received)
346
                return;
347

    
348
        res = __separate_by_order(sum / prefs->status->received);
349

    
350
        fprintf(stdout, "Mean Time:    %3u. %03u  %03u  %03u\n",
351
                        res.s, res.ms, res.us, res.ns);
352
        fflush(stdout);
353

    
354
        //TODO: Add std
355

    
356
        print_io_stats(prefs, sum);
357
}
358

    
359
void print_progress(struct bench *prefs)
360
{
361
        int lines = 6;
362

    
363
        if ((prefs->op == X_READ) && (GET_FLAG(VERIFY, prefs->flags) != VERIFY_NO))
364
                lines++;
365

    
366
        fprintf(stdout, "\033[%dA\033[J", lines);
367
        print_stats(prefs);
368
}
369

    
370
/**************************\
371
 * Benchmarking functions *
372
\**************************/
373

    
374
void create_id(unsigned long seed)
375
{
376
        if (seed > pow(10, 9))
377
                XSEGLOG2(&lc, W, "Seed larger than 10^9, only its first 9 digits will "
378
                                "be used\n");
379

    
380
        //nanoseconds can't be more than 9 digits
381
        snprintf(global_id, IDLEN + 1, "bench-%09lu", seed);
382
}
383

    
384
void create_target(struct bench *prefs, struct xseg_request *req,
385
                uint64_t new)
386
{
387
        struct xseg *xseg = prefs->peer->xseg;
388
        char *req_target;
389
        char buf[TARGETLEN + 1];
390

    
391
        req_target = xseg_get_target(xseg, req);
392

    
393
        //For read/write, the target object may not correspond to `new`, which is
394
        //actually the chunk number.
395
        new = __get_object(prefs, new);
396
        snprintf(buf, TARGETLEN + 1, "%s-%016lu", global_id, new);
397
        strncpy(req_target, buf, TARGETLEN);
398
        XSEGLOG2(&lc, D, "Target name of request is %s\n", buf);
399
}
400

    
401

    
402
uint64_t determine_next(struct bench *prefs)
403
{
404
        if (GET_FLAG(PATTERN, prefs->flags) == PATTERN_SEQ)
405
                return prefs->status->submitted;
406
        else
407
                return lfsr_next(prefs->lfsr);
408
}
409

    
410
uint64_t calculate_offset(struct bench *prefs, uint64_t new)
411
{
412
        if (prefs->ts > 0)
413
                return (new * prefs->bs) % prefs->os;
414
        else
415
                return 0;
416
}
417

    
418
uint64_t calculate_prog_quantum(struct bench *prefs)
419
{
420
        return round((double)prefs->status->max / 20.0);
421
}
422

    
423

    
424
/*
425
 * ***********************************************
426
 * `create_chunk` handles 3 identifiers:
427
 * 1. The benchmark's global_id
428
 * 2. The object's number
429
 * 3. The chunk offset in the object
430
 *
431
 * ************************************************
432
 * `_create_chunk_full` takes the above 3 identifiers and feeds them as seeds
433
 * in 63-bit LFSRs. The numbers generated are written consecutively in chunk's
434
 * memory range. For example, for a 72-byte chunk:
435
 *
436
 * || 1 | 2 | 3 | 1 | 2 | 3 | 1 | 2 | 3 ||
437
 *  ^   8  16  24  32  40  48  56  64   ^
438
 *  |                                   |
439
 *  |                                   |
440
 * start                               end
441
 *
442
 * 1,2,3 differ between each iteration
443
 *
444
 * **************************************************
445
 * `_create_chunk_meta` simply writes the above 3 ids in the start and end of
446
 * the chunk's memory range, so it should be much faster (but less safe)
447
 *
448
 * **************************************************
449
 * In both cases, special care is taken not to exceed the chunk's memory range.
450
 * Also, the bare minimum chunk to verify should be 48 bytes. This limit is set
451
 * by _create_chunk_meta, which expects to write in a memory at least this big.
452
 */
453
static int _readwrite_chunk_full(struct xseg *xseg, struct xseg_request *req,
454
                uint64_t id, uint64_t object)
455
{
456
        struct bench_lfsr id_lfsr;
457
        struct bench_lfsr obj_lfsr;
458
        struct bench_lfsr off_lfsr;
459
        uint64_t *d = (uint64_t *)xseg_get_data(xseg, req);
460
        uint64_t s = req->size;
461

    
462
        /* Create 63-bit LFSRs */
463
        lfsr_init(&id_lfsr, 0x7FFFFFFF, id, 0);
464
        lfsr_init(&obj_lfsr, 0x7FFFFFFF, object, 0);
465
        lfsr_init(&off_lfsr, 0x7FFFFFFF, req->offset, 0);
466

    
467
        if (s < sizeof(struct signature)) {
468
                XSEGLOG2(&lc, E, "Too small chunk size (%lu butes). Leaving.", s);
469
                return 1;
470
        }
471

    
472
        /*
473
         * Every write operation has its read counterpart which, if it finds any
474
         * corruption, returns 1
475
         */
476

    
477
        if (req->op == X_WRITE) {
478
                __write_sig(&id_lfsr, d, s, 0);
479
                __write_sig(&obj_lfsr, d, s, 1);
480
                __write_sig(&off_lfsr, d, s, 2);
481
        } else {
482
                if (__read_sig(&id_lfsr, d, s, 0))
483
                        return 1;
484
                if (__read_sig(&obj_lfsr, d, s, 1))
485
                        return 1;
486
                if(__read_sig(&off_lfsr, d, s, 2))
487
                        return 1;
488
        }
489

    
490
        return 0;
491
}
492

    
493
static int _readwrite_chunk_meta(struct xseg *xseg, struct xseg_request *req,
494
                uint64_t id, uint64_t object)
495
{
496
        char *d = xseg_get_data(xseg, req);
497
        uint64_t s = req->size;
498
        struct signature sig;
499
        int sig_s = sizeof(struct signature);
500
        int r = 0;
501

    
502
        sig.id = id;
503
        sig.object = object;
504
        sig.offset = req->offset;
505

    
506
        if (s < sig_s) {
507
                XSEGLOG2(&lc, E, "Too small chunk size (%lu butes). Leaving.", s);
508
                return 1;
509
        }
510

    
511
        //PRINT_SIG(expected, (&sig));
512
        /* Read/Write chunk signature both at its start and at its end */
513
        if (req->op == X_WRITE) {
514
                memcpy(d, &sig, sig_s);
515
                memcpy(d + s - sig_s, &sig, sig_s);
516
        } else {
517
                if (memcmp(d, &sig, sig_s))
518
                        r = 1;
519
                else if (memcmp(d + s - sig_s, &sig, sig_s))
520
                        r = 1;
521
        }
522
        //PRINT_SIG(start, d);
523
        //PRINT_SIG(end, (d + s - sig_s));
524
        return r;
525
}
526

    
527
/*
528
 * We want these functions to be as fast as possible in case we haven't asked
529
 * for verification
530
 * TODO: Make them prettier but keep the speed of this implementation
531
 */
532
void create_chunk(struct bench *prefs, struct xseg_request *req, uint64_t new)
533
{
534
        struct xseg *xseg = prefs->peer->xseg;
535
        uint64_t id;
536
        uint64_t object;
537
        int verify;
538

    
539
        verify = GET_FLAG(VERIFY, prefs->flags);
540
        switch (verify) {
541
                case VERIFY_NO:
542
                        break;
543
                case VERIFY_META:
544
                        id = __get_id();
545
                        object = __get_object(prefs, new);
546
                        _readwrite_chunk_meta(xseg, req, id, object);
547
                        break;
548
                case VERIFY_FULL:
549
                        id = __get_id();
550
                        object = __get_object(prefs, new);
551
                        _readwrite_chunk_full(xseg, req, id, object);
552
                        break;
553
                default:
554
                        XSEGLOG2(&lc, W, "Unexpected verification mode: %d\n", verify);
555
        }
556
}
557

    
558
int read_chunk(struct bench *prefs, struct xseg_request *req)
559
{
560
        struct xseg *xseg = prefs->peer->xseg;
561
        uint64_t id;
562
        uint64_t object;
563
        char *target;
564
        int verify;
565
        int r = 0;
566

    
567
        verify = GET_FLAG(VERIFY, prefs->flags);
568
        switch (verify) {
569
                case VERIFY_NO:
570
                        break;
571
                case VERIFY_META:
572
                        id = __get_id();
573
                        target = xseg_get_target(xseg, req);
574
                        object = __get_object_from_name(target);
575
                        r = _readwrite_chunk_meta(xseg, req, id, object);
576
                        break;
577
                case VERIFY_FULL:
578
                        id = __get_id();
579
                        target = xseg_get_target(xseg, req);
580
                        object = __get_object_from_name(target);
581
                        r = _readwrite_chunk_full(xseg, req, id, object);
582
                        break;
583
                default:
584
                        XSEGLOG2(&lc, W, "Unexpected verification mode: %d\n", verify);
585
        }
586
        return r;
587
}