bench: Add --prefix option
[archipelago] / xseg / peers / user / bench-utils.c
index d861697..77416d9 100644 (file)
 #include <math.h>
 #include <string.h>
 
-#define PRINT_SIG(__who, __sig)                                                                        \
-       printf("%s (%lu): id %lu, object %lu, offset %lu\n",            \
-                       #__who, (uint64_t)(__sig),                                                      \
-                       ((struct signature *)__sig)->id,                                        \
-                       ((struct signature *)__sig)->object,                            \
+#define PRINT_SIG(__who, __sig)                                                \
+       fprintf(stdout, "%s (%lu): id %lu, object %lu, offset %lu\n",   \
+                       #__who, (uint64_t)(__sig),                      \
+                       ((struct signature *)__sig)->id,                \
+                       ((struct signature *)__sig)->object,            \
                        ((struct signature *)__sig)->offset);
 
-/******************************\
- * Static miscellaneous tools *
-\******************************/
 struct timespec delay = {0, 4000000};
 
-static inline uint64_t _get_id()
+__attribute__ ((unused))
+void inspect_obv(struct object_vars *obv)
 {
-       return atol(global_id + 6); /* cut the "bench-" part*/
+       XSEGLOG2(&lc, D, "Struct object vars:\n"
+                       "\tname: %s (%d),\n"
+                       "\tprefix: %s (%d),\n"
+                       "\tseed: %lu (%d),\n"
+                       "\tobjnum: %lu (%d)",
+                       obv->name, obv->namelen, obv->prefix, obv->prefixlen,
+                       obv->seed, obv->seedlen,
+                       obv->objnum, obv->objnumlen);
 }
 
-static inline uint64_t _get_object_from_name(char *name)
+uint64_t __get_object(struct bench *prefs, uint64_t new)
 {
-       return atol(name + IDLEN); /* cut the "bench-908135-" part*/
+       if (prefs->ts > 0)
+               new = new / (prefs->os / prefs->bs);
+       return new;
 }
 
-static inline uint64_t _get_object(struct bench *prefs, uint64_t new)
+/******************************\
+ * Static miscellaneous tools *
+\******************************/
+static inline uint64_t __get_object_from_name(struct object_vars *obv,
+               char *name)
 {
-       return new / (prefs->os / prefs->bs);
+       /* In case of --objname switch */
+       if (obv->name[0])
+               return 0;
+
+       /* Keep only the object number */
+       return atol(name + obv->namelen - obv->objnumlen);
 }
 
-static inline int _snap_to_bound8(uint64_t space)
+static inline int __snap_to_bound8(uint64_t space)
 {
        return space > 8 ? 8 : space;
 }
 
-static inline double timespec2double(struct timespec num)
+static inline double __timespec2double(struct timespec num)
 {
        return (double) (num.tv_sec * pow(10, 9) + num.tv_nsec);
 }
 
-static inline void write_sig(struct bench_lfsr *sg,    uint64_t *d, uint64_t s,
+static inline void __write_sig(struct bench_lfsr *sg, uint64_t *d, uint64_t s,
                int pos)
 {
        uint64_t i;
@@ -95,16 +111,16 @@ static inline void write_sig(struct bench_lfsr *sg,        uint64_t *d, uint64_t s,
 
        /* Write random numbers (based on global_id) every 24 bytes */
        /* TODO: Should we use memcpy? */
-       for (i = pos; i < s - (3 - pos); i += 3)
+       for (i = pos; i < (s / 8) - (3 - pos); i += 3)
                *(d + i) = lfsr_next(sg);
 
        /* special care for last chunk */
        last_val = lfsr_next(sg);
        space_left = s - (i * 8);
-       memcpy(d + i, &last_val, _snap_to_bound8(space_left));
+       memcpy(d + i, &last_val, __snap_to_bound8(space_left));
 }
 
-static inline int read_sig(struct bench_lfsr *sg, uint64_t *d, uint64_t s,
+static inline int __read_sig(struct bench_lfsr *sg, uint64_t *d, uint64_t s,
                int pos)
 {
        uint64_t i;
@@ -112,14 +128,14 @@ static inline int read_sig(struct bench_lfsr *sg, uint64_t *d, uint64_t s,
        uint64_t space_left;
 
        /* TODO: Should we use memcmp? */
-       for (i = pos; i < s - (3 - pos); i += 3) {
+       for (i = pos; i < (s / 8) - (3 - pos); i += 3) {
                if (*(d + i) != lfsr_next(sg))
                        return 1;
        }
        /* special care for last chunk */
        last_val = lfsr_next(sg);
        space_left = s - (i * 8);
-       if (memcmp(d + i, &last_val, _snap_to_bound8(space_left)))
+       if (memcmp(d + i, &last_val, __snap_to_bound8(space_left)))
                return 1;
 
        return 0;
@@ -129,7 +145,7 @@ static inline int read_sig(struct bench_lfsr *sg, uint64_t *d, uint64_t s,
  * Seperates a double number in seconds, msec, usec, nsec
  * Expects a number in nanoseconds (e.g. a number from timespec2double)
  */
-static struct tm_result separate_by_order(double num)
+static struct tm_result __separate_by_order(double num)
 {
        struct tm_result res;
 
@@ -147,6 +163,37 @@ static struct tm_result separate_by_order(double num)
        return res;
 }
 
+static void __calculate_bw(struct bench *prefs, double iops, struct bw *bw)
+{
+       bw->val = iops * prefs->bs;
+       strcpy(bw->unit, "B/s");
+
+       if (bw->val < 1024)
+               return;
+
+       bw->val = bw->val / 1024;
+       strcpy(bw->unit, "KB/s");
+
+       if (bw->val < 1024)
+               return;
+
+       bw->val = bw->val / 1024;
+       strcpy(bw->unit, "MB/s");
+
+       if (bw->val < 1024)
+               return;
+
+       bw->val = bw->val / 1024;
+       strcpy(bw->unit, "GB/s");
+}
+
+static double __calculate_iops(struct bench *prefs, double elapsed_ns)
+{
+       /* elapsed_ns is in nanoseconds, so we convert it to seconds */
+       double elapsed = elapsed_ns / pow(10,9);
+       return (prefs->status->received / elapsed);
+}
+
 /******************************\
  * Argument-parsing functions *
 \******************************/
@@ -224,6 +271,24 @@ int read_verify(char *verify)
        return -1;
 }
 
+int read_progress(char *progress)
+{
+       if (strncmp(progress, "no", MAX_ARG_LEN + 1) == 0)
+               return PROGRESS_NO;
+       if (strncmp(progress, "yes", MAX_ARG_LEN + 1) == 0)
+               return PROGRESS_YES;
+       return -1;
+}
+
+int read_ping(char *ping)
+{
+       if (strncmp(ping, "no", MAX_ARG_LEN + 1) == 0)
+               return PING_MODE_OFF;
+       if (strncmp(ping, "yes", MAX_ARG_LEN + 1) == 0)
+               return PING_MODE_ON;
+       return -1;
+}
+
 int read_pattern(char *pattern)
 {
        if (strncmp(pattern, "seq", MAX_ARG_LEN + 1) == 0)
@@ -233,87 +298,142 @@ int read_pattern(char *pattern)
        return -1;
 }
 
+int validate_seed(struct bench *prefs, unsigned long seed)
+{
+       if (seed < pow(10, prefs->objvars->seedlen))
+               return 0;
+       return -1;
+}
+
 /*******************\
  * Print functions *
 \*******************/
 
-void print_stats(struct bench *prefs)
+void print_io_stats(struct bench *prefs, double elapsed)
 {
-       uint64_t remaining;
+       struct bw bw;
+       double iops;
+
+       /*
+        * We could malloc struct bw in __calculate_bw, but it's safer in cases when
+        * there is no memory left.
+        */
+       iops = __calculate_iops(prefs, elapsed);
+       __calculate_bw(prefs, iops, &bw);
 
-       printf("\n");
-       printf("Requests total:     %10lu\n", prefs->status->max);
-       printf("Requests submitted: %10lu\n", prefs->status->submitted);
-       printf("Requests received:  %10lu\n", prefs->status->received);
-       printf("Requests failed:    %10lu\n", prefs->status->failed);
+       fprintf(stdout, "           ~~~~~~~~~~~~~~~~~~~~~~~~\n");
+       if (prefs->op == X_READ || prefs->op == X_WRITE)
+               fprintf(stdout, "Bandwidth:    %.3lf %s\n", bw.val, bw.unit);
+       fprintf(stdout, "IOPS:         %.3lf\n", iops);
+}
+
+void print_stats(struct bench *prefs)
+{
+       fprintf(stdout, "\n"
+                       "Requests total:     %10lu\n"
+                       "Requests submitted: %10lu\n"
+                       "Requests received:  %10lu\n"
+                       "Requests failed:    %10lu\n",
+                       prefs->status->max,
+                       prefs->status->submitted,
+                       prefs->status->received,
+                       prefs->status->failed);
        if ((prefs->op == X_READ) && (GET_FLAG(VERIFY, prefs->flags) != VERIFY_NO))
-               printf("Requests corrupted: %10lu\n", prefs->status->corrupted);
-       printf("\n");
+               fprintf(stdout, "Requests corrupted: %10lu\n", prefs->status->corrupted);
+       fprintf(stdout, "\n");
+       fflush(stdout);
+}
+
+void print_remaining(struct bench *prefs)
+{
+       uint64_t remaining;
 
        remaining = prefs->status->max - prefs->status->received;
        if (remaining)
-               printf("Requests remaining: %10lu\n", remaining);
+               fprintf(stdout, "Requests remaining: %10lu\n", remaining);
        else
-               printf("All requests have been served.\n");
+               fprintf(stdout, "All requests have been served.\n");
+       fflush(stdout);
 }
 
-void print_res(struct bench *prefs, struct timer *tm, char *type)
+void print_res(struct bench *prefs)
 {
-       struct tm_result res;
-       double sum;
-
-       sum = timespec2double(tm->sum);
-       res = separate_by_order(sum);
-
-       printf("\n");
-       printf("              %s\n", type);
-       printf("           ========================\n");
-       printf("             |-s-||-ms-|-us-|-ns-|\n");
-       printf("Total time:   %3u. %03u  %03u  %03u\n",
+       struct timer *tm;
+       struct tm_result res, res_rec;
+       double sum, sum_rec;
+
+       /*  */
+       tm = prefs->total_tm;
+       sum = __timespec2double(tm->sum);
+       res = __separate_by_order(sum);
+
+       fprintf(stdout, "\n");
+       fprintf(stdout, "              Benchmark results\n");
+       fprintf(stdout, "           ========================\n");
+       fprintf(stdout, "             |-s-||-ms-|-us-|-ns-|\n");
+       fprintf(stdout, "Total time:   %3u. %03u  %03u  %03u\n",
                        res.s, res.ms, res.us, res.ns);
 
-       if (!prefs->status->received)
+       if (!prefs->status->received) {
+               fflush(stdout);
                return;
+       }
 
-       res = separate_by_order(sum / prefs->status->received);
+       tm = prefs->rec_tm;
+       if (GET_FLAG(INSANITY, prefs->flags) < tm->insanity)
+               goto flush;
 
-       printf("Mean Time:    %3u. %03u  %03u  %03u\n",
-                       res.s, res.ms, res.us, res.ns);
+       sum_rec = __timespec2double(tm->sum);
+       res_rec = __separate_by_order(sum_rec / prefs->status->received);
 
-       //TODO: Add std
-}
+       fprintf(stdout, "Avg. latency: %3u. %03u  %03u  %03u\n",
+                       res_rec.s, res_rec.ms, res_rec.us, res_rec.ns);
 
-/**************************\
- * Benchmarking functions *
-\**************************/
+flush:
+       print_io_stats(prefs, sum);
+       fflush(stdout);
+}
 
-void create_id(unsigned long seed)
+void print_progress(struct bench *prefs)
 {
-       if (seed > pow(10, 9))
-               XSEGLOG2(&lc, W, "Seed larger than 10^9, only its first 9 digits will "
-                               "be used\n");
+       int lines = 6;
+
+       if ((prefs->op == X_READ) && (GET_FLAG(VERIFY, prefs->flags) != VERIFY_NO))
+               lines++;
 
-       //nanoseconds can't be more than 9 digits
-       snprintf(global_id, IDLEN, "bench-%09lu", seed);
+       fprintf(stdout, "\033[%dA\033[J", lines);
+       print_stats(prefs);
 }
 
-void create_target(struct bench *prefs, struct xseg_request *req,
-               uint64_t new)
+/**************************\
+ * Benchmarking functions *
+\**************************/
+
+void create_target(struct bench *prefs, struct xseg_request *req)
 {
        struct xseg *xseg = prefs->peer->xseg;
+       struct object_vars *obv = prefs->objvars;
        char *req_target;
 
        req_target = xseg_get_target(xseg, req);
 
-       //For read/write, the target object does not correspond to `new`, which is
-       //actually the chunk number.
-       if (prefs->op == X_READ || prefs->op == X_WRITE)
-               new = _get_object(prefs, new);
-       snprintf(req_target, TARGETLEN, "%s-%016lu", global_id, new);
+       /*
+        * For read/write, the target object may not correspond to `new`, which
+        * is actually the chunk number.
+        * Also, we use one extra byte while writting the target's name to store
+        * the null character and not overflow, but this will not be part of the
+        * target's name
+        */
+       if (obv->prefix[0]) {
+               snprintf(req_target, obv->namelen + 1, "%s-%0*lu-%0*lu",
+                               obv->prefix, obv->seedlen, obv->seed,
+                               obv->objnumlen, obv->objnum);
+       } else {
+               strncpy(req_target, obv->name, obv->namelen);
+       }
        XSEGLOG2(&lc, D, "Target name of request is %s\n", req_target);
 }
 
-
 uint64_t determine_next(struct bench *prefs)
 {
        if (GET_FLAG(PATTERN, prefs->flags) == PATTERN_SEQ)
@@ -322,6 +442,20 @@ uint64_t determine_next(struct bench *prefs)
                return lfsr_next(prefs->lfsr);
 }
 
+uint64_t calculate_offset(struct bench *prefs, uint64_t new)
+{
+       if (prefs->ts > 0)
+               return (new * prefs->bs) % prefs->os;
+       else
+               return 0;
+}
+
+uint64_t calculate_prog_quantum(struct bench *prefs)
+{
+       return round((double)prefs->status->max / 20.0);
+}
+
+
 /*
  * ***********************************************
  * `create_chunk` handles 3 identifiers:
@@ -330,7 +464,7 @@ uint64_t determine_next(struct bench *prefs)
  * 3. The chunk offset in the object
  *
  * ************************************************
- * `_create_chunk_full` takes the above 3 identifiers and feeds them as seeds
+ * `readwrite_chunk_full` takes the above 3 identifiers and feeds them as seeds
  * in 63-bit LFSRs. The numbers generated are written consecutively in chunk's
  * memory range. For example, for a 72-byte chunk:
  *
@@ -349,21 +483,29 @@ uint64_t determine_next(struct bench *prefs)
  * **************************************************
  * In both cases, special care is taken not to exceed the chunk's memory range.
  * Also, the bare minimum chunk to verify should be 48 bytes. This limit is set
- * by _create_chunk_meta, which expects to write in a memory at least this big.
+ * by readwrite_chunk_meta, which expects to write in a memory at least this
+ * big.
+ *
+ * **************************************************
+ * Note: The diagram above also represents the x86_64's endianness.
+ * Endianness must be taken into careful consideration when examining a memory
+ * chunk.
  */
-static int _readwrite_chunk_full(struct xseg *xseg, struct xseg_request *req,
-               uint64_t id, uint64_t object)
+static int readwrite_chunk_full(struct bench *prefs, struct xseg_request *req)
 {
        struct bench_lfsr id_lfsr;
        struct bench_lfsr obj_lfsr;
        struct bench_lfsr off_lfsr;
+       struct xseg *xseg = prefs->peer->xseg;
+       uint64_t id = prefs->objvars->seed;
+       uint64_t object = prefs->objvars->objnum;
        uint64_t *d = (uint64_t *)xseg_get_data(xseg, req);
        uint64_t s = req->size;
 
        /* Create 63-bit LFSRs */
-       lfsr_init(&id_lfsr, 0x7FFFFFFF, id, 0);
-       lfsr_init(&obj_lfsr, 0x7FFFFFFF, object, 0);
-       lfsr_init(&off_lfsr, 0x7FFFFFFF, req->offset, 0);
+       lfsr_init(&id_lfsr, 0x7FFFFFFFFFFFFFFF, id, 0);
+       lfsr_init(&obj_lfsr, 0x7FFFFFFFFFFFFFFF, object, 0);
+       lfsr_init(&off_lfsr, 0x7FFFFFFFFFFFFFFF, req->offset, 0);
 
        if (s < sizeof(struct signature)) {
                XSEGLOG2(&lc, E, "Too small chunk size (%lu butes). Leaving.", s);
@@ -376,27 +518,29 @@ static int _readwrite_chunk_full(struct xseg *xseg, struct xseg_request *req,
         */
 
        if (req->op == X_WRITE) {
-               write_sig(&id_lfsr, d, s, 1);
-               write_sig(&obj_lfsr, d, s, 2);
-               write_sig(&off_lfsr, d, s, 3);
+               __write_sig(&id_lfsr, d, s, 0);
+               __write_sig(&obj_lfsr, d, s, 1);
+               __write_sig(&off_lfsr, d, s, 2);
        } else {
-               if (read_sig(&id_lfsr, d, s, 1))
+               if (__read_sig(&id_lfsr, d, s, 0))
                        return 1;
-               if (read_sig(&obj_lfsr, d, s, 2))
+               if (__read_sig(&obj_lfsr, d, s, 1))
                        return 1;
-               if(read_sig(&off_lfsr, d, s, 3))
+               if(__read_sig(&off_lfsr, d, s, 2))
                        return 1;
        }
 
        return 0;
 }
 
-static int _readwrite_chunk_meta(struct xseg *xseg, struct xseg_request *req,
-               uint64_t id, uint64_t object)
+static int readwrite_chunk_meta(struct bench *prefs, struct xseg_request *req)
 {
+       struct xseg *xseg = prefs->peer->xseg;
+       struct signature sig;
+       uint64_t id = prefs->objvars->seed;
+       uint64_t object = prefs->objvars->objnum;
        char *d = xseg_get_data(xseg, req);
        uint64_t s = req->size;
-       struct signature sig;
        int sig_s = sizeof(struct signature);
        int r = 0;
 
@@ -405,7 +549,8 @@ static int _readwrite_chunk_meta(struct xseg *xseg, struct xseg_request *req,
        sig.offset = req->offset;
 
        if (s < sig_s) {
-               XSEGLOG2(&lc, E, "Too small chunk size (%lu butes). Leaving.", s);
+               XSEGLOG2(&lc, E, "Too small chunk size (%lu butes). "
+                               "Leaving.", s);
                return 1;
        }
 
@@ -428,13 +573,9 @@ static int _readwrite_chunk_meta(struct xseg *xseg, struct xseg_request *req,
 /*
  * We want these functions to be as fast as possible in case we haven't asked
  * for verification
- * TODO: Make them prettier but keep the speed of this implementation
  */
 void create_chunk(struct bench *prefs, struct xseg_request *req, uint64_t new)
 {
-       struct xseg *xseg = prefs->peer->xseg;
-       uint64_t id;
-       uint64_t object;
        int verify;
 
        verify = GET_FLAG(VERIFY, prefs->flags);
@@ -442,25 +583,23 @@ void create_chunk(struct bench *prefs, struct xseg_request *req, uint64_t new)
                case VERIFY_NO:
                        break;
                case VERIFY_META:
-                       id = _get_id();
-                       object = _get_object(prefs, new);
-                       _readwrite_chunk_meta(xseg, req, id, object);
+                       inspect_obv(prefs->objvars);
+                       readwrite_chunk_meta(prefs, req);
                        break;
                case VERIFY_FULL:
-                       id = _get_id();
-                       object = _get_object(prefs, new);
-                       _readwrite_chunk_full(xseg, req, id, object);
+                       inspect_obv(prefs->objvars);
+                       readwrite_chunk_full(prefs, req);
                        break;
                default:
-                       XSEGLOG2(&lc, W, "Unexpected verification mode: %d\n", verify);
+                       XSEGLOG2(&lc, W, "Unexpected verification mode: %d\n",
+                                       verify);
        }
 }
 
 int read_chunk(struct bench *prefs, struct xseg_request *req)
 {
        struct xseg *xseg = prefs->peer->xseg;
-       uint64_t id;
-       uint64_t object;
+       struct object_vars *obv = prefs->objvars;
        char *target;
        int verify;
        int r = 0;
@@ -470,21 +609,20 @@ int read_chunk(struct bench *prefs, struct xseg_request *req)
                case VERIFY_NO:
                        break;
                case VERIFY_META:
-                       id = _get_id();
                        target = xseg_get_target(xseg, req);
-                       object = _get_object_from_name(target);
-                       r = _readwrite_chunk_meta(xseg, req, id, object);
+                       obv->objnum = __get_object_from_name(obv, target);
+                       inspect_obv(prefs->objvars);
+                       r = readwrite_chunk_meta(prefs, req);
                        break;
                case VERIFY_FULL:
-                       id = _get_id();
                        target = xseg_get_target(xseg, req);
-                       object = _get_object_from_name(target);
-                       r = _readwrite_chunk_full(xseg, req, id, object);
+                       obv->objnum = __get_object_from_name(obv, target);
+                       inspect_obv(prefs->objvars);
+                       r = readwrite_chunk_full(prefs, req);
                        break;
                default:
-                       XSEGLOG2(&lc, W, "Unexpected verification mode: %d\n", verify);
+                       XSEGLOG2(&lc, W, "Unexpected verification mode: %d\n",
+                                       verify);
        }
        return r;
 }
-
-