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)
+ if (bw->val < 1024) {
+ strcpy(bw->unit, "B/s");
return;
+ }
bw->val = bw->val / 1024;
- strcpy(bw->unit, "KB/s");
- if (bw->val < 1024)
+ if (bw->val < 1024) {
+ strcpy(bw->unit, "KB/s");
return;
+ }
bw->val = bw->val / 1024;
- strcpy(bw->unit, "MB/s");
- if (bw->val < 1024)
+ if (bw->val < 1024) {
+ strcpy(bw->unit, "MB/s");
return;
+ }
bw->val = bw->val / 1024;
strcpy(bw->unit, "GB/s");
}
-static double __calculate_iops(struct bench *prefs, double elapsed_ns)
+static double __calculate_iops(uint64_t requests, 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);
+ return (requests / elapsed);
}
/******************************\
{
if (strncmp(progress, "no", MAX_ARG_LEN + 1) == 0)
return PROGRESS_NO;
- if (strncmp(progress, "yes", MAX_ARG_LEN + 1) == 0)
- return PROGRESS_YES;
+ if (strncmp(progress, "req", MAX_ARG_LEN + 1) == 0)
+ return PROGRESS_REQ;
+ if (strncmp(progress, "io", MAX_ARG_LEN + 1) == 0)
+ return PROGRESS_IO;
+ if (strncmp(progress, "both", MAX_ARG_LEN + 1) == 0)
+ return PROGRESS_BOTH;
return -1;
}
+/*
+ * Read interval in percentage or raw mode and return its length (in requests)
+ * If syntax is invalid, return 0.
+ */
+uint64_t read_interval(struct bench *prefs, char *str_interval)
+{
+ char *unit = NULL;
+ uint64_t interval;
+
+ interval = strtoll(str_interval, &unit, 10);
+
+ /* If interval is raw number of requests */
+ if (!unit[0] && interval < prefs->status->max)
+ return interval;
+
+ /* If interval is percentage of max requests */
+ if (strncmp(unit, "%", MAX_ARG_LEN + 1) == 0 &&
+ interval > 0 && interval < 100)
+ return calculate_interval(prefs, interval);
+
+ return 0;
+}
+
int read_ping(char *ping)
{
if (strncmp(ping, "no", MAX_ARG_LEN + 1) == 0)
* Print functions *
\*******************/
-void clear_lines(struct bench *prefs)
+uint64_t calculate_interval(struct bench *prefs, uint64_t percentage)
{
- int lines = 6;
+ uint64_t interval = round((double)prefs->status->max *
+ (double)percentage / 100);
- if ((prefs->op == X_READ) &&
- (GET_FLAG(VERIFY, prefs->flags) != VERIFY_NO))
- lines++;
+ if (!interval)
+ interval = 1;
- /* TODO: Add bandwidth */
+ return interval;
+}
+
+int calculate_report_lines(struct bench *prefs)
+{
+ int progress = GET_FLAG(PROGRESS, prefs->flags);
+ int lines = 0;
+
+ if (progress == PROGRESS_REQ || progress == PROGRESS_BOTH) {
+ lines = 6;
+ if ((GET_FLAG(VERIFY, prefs->flags) != VERIFY_NO) &&
+ (prefs->op == X_READ))
+ lines++;
+ }
+ if (progress == PROGRESS_IO || progress == PROGRESS_BOTH) {
+ lines += 1;
+ if (prefs->op == X_READ || prefs->op == X_WRITE)
+ lines++;
+ }
+
+ return lines;
+}
+void clear_report_lines(int lines)
+{
fprintf(stdout, "\033[%dA\033[J", lines);
}
-void print_io_stats(struct bench *prefs, double elapsed)
+void print_divider()
{
+ fprintf(stdout, " ~~~~~~~~~~~~~~~~~~~~~~~~\n");
+}
+
+void print_io_stats(struct bench *prefs)
+{
+ struct timer *tm = prefs->total_tm;
struct bw bw;
+ double elapsed;
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);
+ if (!prefs->status->received) {
+ if (prefs->op == X_READ || prefs->op == X_WRITE)
+ fprintf(stdout, "Bandwidth: NaN\n");
+ fprintf(stdout, "IOPS: NaN\n");
+ return;
+ }
+
+ elapsed = __timespec2double(tm->elapsed_time);
+ iops = __calculate_iops(prefs->rep->interval, elapsed);
__calculate_bw(prefs, iops, &bw);
- 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)
+void print_req_stats(struct bench *prefs)
{
fprintf(stdout, "\n"
"Requests total: %10lu\n"
prefs->status->submitted,
prefs->status->received,
prefs->status->failed);
- if ((prefs->op == X_READ) && (GET_FLAG(VERIFY, prefs->flags) != VERIFY_NO))
- fprintf(stdout, "Requests corrupted: %10lu\n", prefs->status->corrupted);
+ if ((prefs->op == X_READ) &&
+ (GET_FLAG(VERIFY, prefs->flags) != VERIFY_NO))
+ fprintf(stdout, "Requests corrupted: %10lu\n",
+ prefs->status->corrupted);
fprintf(stdout, "\n");
- fflush(stdout);
}
void print_remaining(struct bench *prefs)
fprintf(stdout, "Requests remaining: %10lu\n", remaining);
else
fprintf(stdout, "All requests have been served.\n");
- fflush(stdout);
}
-void print_res(struct bench *prefs)
+void print_total_res(struct bench *prefs)
{
- struct timer *tm;
- struct tm_result res, res_rec;
- double sum, sum_rec;
+ struct timer *tm = prefs->total_tm;
+ struct tm_result res;
+ double sum;
- /* */
- tm = prefs->total_tm;
sum = __timespec2double(tm->sum);
res = __separate_by_order(sum);
fprintf(stdout, " |-s-||-ms-|-us-|-ns-|\n");
fprintf(stdout, "Total time: %3u. %03u %03u %03u\n",
res.s, res.ms, res.us, res.ns);
+}
+
+void print_rec_res(struct bench *prefs)
+{
+ struct timer *tm = prefs->rec_tm;
+ struct tm_result res;
+ double sum;
if (!prefs->status->received) {
- fflush(stdout);
+ fprintf(stdout, "Avg. latency: NaN\n");
return;
}
- tm = prefs->rec_tm;
- if (GET_FLAG(INSANITY, prefs->flags) < tm->insanity)
- goto flush;
-
- sum_rec = __timespec2double(tm->sum);
- res_rec = __separate_by_order(sum_rec / prefs->status->received);
+ sum = __timespec2double(tm->sum);
+ res = __separate_by_order(sum / prefs->status->received);
fprintf(stdout, "Avg. latency: %3u. %03u %03u %03u\n",
- res_rec.s, res_rec.ms, res_rec.us, res_rec.ns);
+ res.s, res.ms, res.us, res.ns);
+}
+
+static void __print_progress(struct bench *prefs)
+{
+ int progress = GET_FLAG(PROGRESS, prefs->flags);
-flush:
- print_io_stats(prefs, sum);
+ if (progress == PROGRESS_REQ || progress == PROGRESS_BOTH)
+ print_req_stats(prefs);
+ if (progress == PROGRESS_IO || progress == PROGRESS_BOTH)
+ print_io_stats(prefs);
fflush(stdout);
}
+void print_dummy_progress(struct bench *prefs)
+{
+ __print_progress(prefs);
+}
+
void print_progress(struct bench *prefs)
{
- clear_lines(prefs);
- print_stats(prefs);
+ timer_stop(prefs, prefs->total_tm, NULL);
+ clear_report_lines(prefs->rep->lines);
+ __print_progress(prefs);
+ timer_start(prefs, prefs->total_tm);
}
/**************************\
return 0;
}
-uint64_t calculate_prog_quantum(struct bench *prefs)
-{
- return round((double)prefs->status->max / 20.0);
-}
-
-
/*
* ***********************************************
* `create_chunk` handles 3 identifiers:
* c) If we are not in ping mode
* d) If we have been asked to terminate
*/
-#define CAN_SEND_REQUEST(__p) \
+#define CAN_SEND_REQUEST(__p) \
((__p->status->submitted - __p->status->received < __p->iodepth) && \
- (__p->status->submitted < __p->status->max) && \
- (GET_FLAG(PING, __p->flags) == PING_MODE_OFF) && \
+ (__p->status->submitted < __p->status->max) && \
+ (GET_FLAG(PING, __p->flags) == PING_MODE_OFF) && \
!isTerminate())
-#define CAN_VERIFY(__p) \
+#define CAN_VERIFY(__p) \
((GET_FLAG(VERIFY, __p->flags) != VERIFY_NO) && __p->op == X_READ)
-#define CAN_PRINT_PROGRESS(__p, __q) \
- ((GET_FLAG(PROGRESS, __p->flags) == PROGRESS_YES) && \
- (__p->status->received == __q))
+#define CAN_PRINT_PROGRESS(__p, __nr) \
+ ((GET_FLAG(PROGRESS, __p->flags) != PROGRESS_NO) && \
+ (GET_FLAG(PING, __p->flags) == PING_MODE_OFF) && \
+ (__p->status->received == __nr))
void custom_peer_usage()
{
fprintf(stderr, "Custom peer options: \n"
" --------------------------------------------\n"
- " -op | None | XSEG operation [read|write|info|delete]\n"
+ " -op | None | XSEG operation:\n"
+ " | | [read|write|info|delete]\n"
" --pattern | None | I/O pattern [seq|rand]\n"
- " --verify | no | Verify written requests [no|meta|full]\n"
+ " --verify | no | Verify written requests:\n"
+ " | | [no|meta|full]\n"
" -rc | None | Request cap\n"
" -to | None | Total objects\n"
" -ts | None | Total I/O size\n"
" --seed | None | Initialize LFSR and target names\n"
" --insanity| sane | Adjust insanity level of benchmark:\n"
" | | [sane|eccentric|manic|paranoid]\n"
- " --progress| yes | Show progress of requests [yes|no]\n"
- " --ping | yes | Ping target before starting benchmark\n"
+ " --progress| both | Show progress of benchmark:\n"
+ " | | [req|io|both|no]\n"
+ " --interval| 5%% | Intervals at which progress is shown\n"
+ " --ping | yes | Ping target before starting:\n"
" | | [yes|no]\n"
- " --prefix | 'bench' | Add a common prefix to all object names\n"
- " --objname | 'bench' | Use only one object with this name\n"
+ " --prefix | bench | Add a common prefix to all object names\n"
+ " --objname | None | Use only one object with this name\n"
"\n"
"Additional information:\n"
" --------------------------------------------\n"
" a. -to option is strictly restricted to 1\n"
" b. -ts option defaults to (and can't be larger than)\n"
" the object size (-os argument)\n"
- " c. --prefix is must be unused. If used, it produces an "
+ " c. --prefix is must be unused. If used, it produces an\n"
" error to alert the user\n"
+ "\n"
+ " * The progress report is printed by default at intervals of\n"
+ " 5%%.\n"
+ " There are three progress types:\n"
+ "\n"
+ " a. req: it prints the request status so far i.e. how many\n"
+ " requests have been subitted, received, failed etc.\n"
+ " b. io: it prints the bandwidth and IOPS status of the\n"
+ " elapsed 5%% of the benchmark\n"
+ " c. both: it combines the output of <req> and <io>.\n"
+ "\n"
+ " * Interval is commonly a percentage of max requests. This\n"
+ " means that when a user gives:\n"
+ " --interval 33%%\n"
+ "\n"
+ " the progress report will be printed 3 times during the\n"
+ " benchmark. Else, if the user wants to, he/she can give:\n"
+ " --interval 1234\n"
+ "\n"
+ " and the progress report will be printed every 1234\n"
+ " requests.\n"
"\n");
}
char insanity[MAX_ARG_LEN + 1];
char verify[MAX_ARG_LEN + 1];
char progress[MAX_ARG_LEN + 1];
+ char interval[MAX_ARG_LEN + 1];
char ping[MAX_ARG_LEN + 1];
char prefix[XSEG_MAX_TARGETLEN + 1];
char objname[XSEG_MAX_TARGETLEN + 1];
verify[0] = 0;
request_cap[0] = 0;
progress[0] = 0;
+ interval[0] = 0;
ping[0] = 0;
prefix[0] = 0;
objname[0] = 0;
}
memset(prefs->objvars, 0, sizeof(struct object_vars));
+ /* allocate struct object_name */
+ prefs->rep = malloc(sizeof(struct progress_report));
+ if (!prefs->rep) {
+ perror("malloc");
+ goto progress_report_fail;
+ }
+ memset(prefs->rep, 0, sizeof(struct progress_report));
+
/* allocate a struct timespec for each peer request */
for (j = 0; j < peer->nr_ops; j++) {
ts = malloc(sizeof(struct timespec));
READ_ARG_STRING("--insanity", insanity, MAX_ARG_LEN);
READ_ARG_STRING("--verify", verify, MAX_ARG_LEN);
READ_ARG_STRING("--progress", progress, MAX_ARG_LEN);
+ READ_ARG_STRING("--interval", interval, MAX_ARG_LEN);
READ_ARG_STRING("--ping", ping, MAX_ARG_LEN);
READ_ARG_STRING("--prefix", prefix, XSEG_MAX_TARGETLEN);
READ_ARG_STRING("--objname", objname, XSEG_MAX_TARGETLEN);
/* Benchmarking progress printing is on by default */
if (!progress[0])
- strcpy(progress, "yes");
+ strcpy(progress, "both");
r = read_progress(progress);
if (r < 0) {
XSEGLOG2(&lc, E, "Invalid syntax: --progress %s\n", progress);
goto arg_fail;
}
SET_FLAG(PROGRESS, prefs->flags, r);
+ prefs->rep->lines = calculate_report_lines(prefs);
+
+ /*
+ * Progress report interval definition makes no sense with disabled
+ * progress reports.
+ */
+ if ((GET_FLAG(PROGRESS, prefs->flags) == PROGRESS_NO) &&
+ interval[0]) {
+ XSEGLOG2(&lc, E, "Cannot define progress interval without "
+ "enabling progress report\n");
+ goto arg_fail;
+ }
+
+ if (GET_FLAG(PROGRESS, prefs->flags) != PROGRESS_NO) {
+ /* By default, we print every 5% */
+ if (!interval[0])
+ strcpy(interval, "5%");
+ prefs->rep->interval = read_interval(prefs, interval);
+ if (prefs->rep->interval == 0) {
+ XSEGLOG2(&lc, E, "Invalid syntax: --interval %s\n",
+ interval);
+ goto arg_fail;
+ }
+ }
/* Pinging the target peer is on by default */
if (!ping[0])
for (; j >= 0; j--) {
free(peer->peer_reqs[j].priv);
}
+progress_report_fail:
+ free(prefs->rep);
object_name_fail:
free(prefs->objvars);
status_fail:
xport portno_end = peer->portno_end;
pid_t pid = syscall(SYS_gettid);
uint64_t threshold=1000/(1 + portno_end - portno_start);
- uint64_t cached_prog_quantum = 0;
- uint64_t prog_quantum = 0;
- int r;
+ uint64_t next_report = prefs->rep->interval;
uint64_t loops;
-
- if (GET_FLAG(PROGRESS, prefs->flags) == PROGRESS_YES) {
- prog_quantum = calculate_prog_quantum(prefs);
- cached_prog_quantum = prog_quantum;
- print_stats(prefs);
- }
+ int r;
XSEGLOG2(&lc, I, "%s has tid %u.\n",id, pid);
xseg_init_local_signal(xseg, peer->portno_start);
+ if (GET_FLAG(PROGRESS, prefs->flags) != PROGRESS_NO)
+ print_dummy_progress(prefs);
+
/* If no ping is going to be sent, we can begin the benchmark now. */
- if (GET_FLAG(PING, prefs->flags) == PING_MODE_OFF)
- timer_start(prefs, prefs->total_tm);
- else
+ if (GET_FLAG(PING, prefs->flags) == PING_MODE_ON)
send_ping_request(peer, prefs);
+ else
+ timer_start(prefs, prefs->total_tm);
send_request:
while (!(isTerminate() && all_peer_reqs_free(peer))) {
while (CAN_SEND_REQUEST(prefs)) {
xseg_cancel_wait(xseg, peer->portno_start);
XSEGLOG2(&lc, D, "...because %lu < %lu && %lu < %lu\n",
- prefs->status->submitted - prefs->status->received,
- prefs->iodepth, prefs->status->received,
- prefs->status->max);
+ prefs->status->submitted - prefs->status->received,
+ prefs->iodepth, prefs->status->received,
+ prefs->status->max);
XSEGLOG2(&lc, D, "Start sending new request\n");
r = send_request(peer, prefs);
if (r < 0)
if (loops == 1)
xseg_prepare_wait(xseg, peer->portno_start);
- if (UNLIKELY(CAN_PRINT_PROGRESS(prefs, prog_quantum))) {
- prog_quantum += cached_prog_quantum;
+ if (CAN_PRINT_PROGRESS(prefs, next_report)) {
print_progress(prefs);
+ next_report += prefs->rep->interval;
}
if (check_ports(peer)) {
- //If an old request has just been acked, the most sensible
- //thing to do is to immediately send a new one
+ //If an old request has just been acked, the
+ //most sensible thing to do is to immediately
+ //send a new one
if (prefs->status->received < prefs->status->max)
goto send_request;
else
return 0;
}
}
- //struct xseg_port *port = xseg_get_port(xseg, portno_start);
- //struct xq *q;
- //q = XPTR_TAKE(port->request_queue, xseg->segment);
- //XSEGLOG2(&lc, I, "%s goes to sleep with %u requests pending\n",
- // id, xq_count(q));
XSEGLOG2(&lc, I, "%s goes to sleep\n", id);
xseg_wait_signal(xseg, 10000000UL);
xseg_cancel_wait(xseg, peer->portno_start);
struct bench *prefs = peer->priv;
//TODO: Measure mean time, standard variation
- if (!prefs->total_tm->completed)
- timer_stop(prefs, prefs->total_tm, NULL);
+ timer_stop(prefs, prefs->total_tm, NULL);
- if (GET_FLAG(PROGRESS, prefs->flags) == PROGRESS_YES)
- clear_lines(prefs);
+ if (GET_FLAG(PROGRESS, prefs->flags) != PROGRESS_NO)
+ clear_report_lines(prefs->rep->lines);
- print_stats(prefs);
+ print_req_stats(prefs);
print_remaining(prefs);
- print_res(prefs);
+ print_total_res(prefs);
+
+ if (GET_FLAG(INSANITY, prefs->flags) >= prefs->rec_tm->insanity)
+ print_rec_res(prefs);
+
+ print_divider();
+ /*
+ * This is kinda hacky but is reasonable.
+ * During the benchmark, we need to calculate the bandwidth within an
+ * interval.
+ * After the benchmark, we need to calculate the total bandwidth. To do
+ * so, we treat the benchmark as one single interval, and that's what
+ * the two lines below do. This way, the same code can aplly to both
+ * cases
+ */
+ prefs->total_tm->elapsed_time = prefs->total_tm->sum;
+ prefs->rep->interval = prefs->status->received;
+ print_io_stats(prefs);
+ fflush(stdout);
return;
}
/* Progress bar option occupies 6th flag bit */
#define PROGRESS_FLAG_POS 5
-#define PROGRESS_BITMASK 1
+#define PROGRESS_BITMASK 3 /* i.e. "11" in binary form */
#define PROGRESS_NO 0
-#define PROGRESS_YES 1
+#define PROGRESS_REQ 1
+#define PROGRESS_IO 2
+#define PROGRESS_BOTH 3
/* Ping option occupies 7th flag bit */
-#define PING_FLAG_POS 6
+#define PING_FLAG_POS 7
#define PING_BITMASK 1
#define PING_MODE_OFF 0
#define PING_MODE_ON 1
/*
* Current bench flags representation:
- * 64 8 7 6 5 4 3 2 1 : bits
- * ... 0 0 0 0 0 0 0 0
- * |_||_||____||____||_|
- * ^ ^ ^ ^ ^
- * | | | | |
- * ping | insanity | pattern
- * progress verify
+ * 64 9 8 7 6 5 4 3 2 1 : bits
+ * ... 0 0 0 0 0 0 0 0 0
+ * |_||____||____||____||_|
+ * ^ ^ ^ ^ ^
+ * | | | | |
+ * ping | insanity | pattern
+ * progress verify
*/
/*
xport src_port;
uint32_t op; //xseg operation
uint64_t flags;
+ unsigned int interval;
struct peerd *peer;
struct req_status *status;
struct bench_lfsr *lfsr;
struct object_vars *objvars;
+ struct progress_report *rep;
struct timer *total_tm; //Total time for benchmark
struct timer *get_tm; //Time for xseg_get_request
struct timer *sub_tm; //Time for xseg_submit_request
uint64_t failed;
};
+struct progress_report {
+ uint64_t prev_recv;
+ uint64_t interval;
+ int lines;
+};
+
/*
* Custom timespec. Made to calculate variance, where we need the square of a
* timespec struct. This struct should be more than enough to hold the square
int read_insanity(char *insanity);
int read_verify(char *insanity);
int read_progress(char *progress);
+uint64_t read_interval(struct bench *prefs, char *str_interval);
int read_ping(char *progress);
-void clear_lines(struct bench *prefs);
-void print_res(struct bench *prefs);
-void print_stats(struct bench *prefs);
+void clear_report_lines(int lines);
+void print_total_res(struct bench *prefs);
+void print_rec_res(struct bench *prefs);
+void print_divider();
+void print_req_stats(struct bench *prefs);
+void print_io_stats(struct bench *prefs);
void print_progress(struct bench *prefs);
+void print_dummy_progress(struct bench *prefs);
void print_remaining(struct bench *prefs);
void create_target(struct bench *prefs, struct xseg_request *req);
void create_chunk(struct bench *prefs, struct xseg_request *req, uint64_t new);
int read_chunk(struct bench *prefs, struct xseg_request *req);
uint64_t determine_next(struct bench *prefs);
uint64_t calculate_offset(struct bench *prefs, uint64_t new);
-uint64_t calculate_prog_quantum(struct bench *prefs);
+uint64_t calculate_interval(struct bench *prefs, uint64_t percentage);
+int calculate_report_lines(struct bench *prefs);
int validate_seed(struct bench *prefs, unsigned long seed);
void inspect_obv(struct object_vars *obv);