//We need a low-latency way to get current time in nanoseconds.
//QUESTION: Is this way the best way?
//RAW means that we trust the system's oscilator isn't screwed up
- if (prefs->insanity < timer->insanity)
+ if (GET_FLAG(INSANITY, prefs->flags) < timer->insanity)
return;
clock_gettime(CLOCK_MONOTONIC_RAW, &timer->start_time);
volatile struct timespec elapsed_time;
struct timespec start_time;
- if (prefs->insanity < timer->insanity)
- goto inc_completed;
+ if (GET_FLAG(INSANITY, prefs->flags)< timer->insanity)
+ return;
/*
* There are timers such as rec_tm whose start_time cannot be trusted and
timer->sum_sq.tv_sec2 += elapsed_time_sq.tv_sec2;
}
#endif
-inc_completed:
//TODO: check if we need to make it volatile
timer->completed++;
#include <math.h>
#include <string.h>
+/******************************\
+ * Static miscellaneous tools *
+\******************************/
struct timespec delay = {0, 4000000};
static inline uint64_t _get_id()
return atol(global_id + 6); /* cut the "bench-" part*/
}
+static inline uint64_t _get_object_from_name(char *name)
+{
+ return atol(name + IDLEN); /* cut the "bench-908135-" part*/
+}
+
static inline uint64_t _get_object(struct bench *prefs, uint64_t new)
{
return new / (prefs->os / prefs->bs);
}
+static inline double timespec2double(struct timespec num)
+{
+ return (double) (num.tv_sec * pow(10, 9) + num.tv_nsec);
+}
+
+/*
+ * 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)
+{
+ struct tm_result res;
+
+ //The format we expect is the following:
+ //
+ // |-s-|-ms-|-us-|-ns|
+ //num = 123 456 789 012 . 000000000000
+ res.s = num / pow(10,9);
+ num = fmod(num, pow(10,9));
+ res.ms = num / pow(10,6);
+ num = fmod(num, pow(10,6));
+ res.us = num / 1000;
+ res.ns = fmod(num, 1000);
+
+ return res;
+}
+
+/******************************\
+ * Argument-parsing functions *
+\******************************/
/*
* Convert string to size in bytes.
/*
* Converts struct timespec to double (units in nanoseconds)
*/
-static double timespec2double(struct timespec num)
-{
- return (double) (num.tv_sec * pow(10, 9) + num.tv_nsec);
-}
-
int read_insanity(char *insanity)
{
if (strncmp(insanity, "sane", MAX_ARG_LEN + 1) == 0)
- return TM_SANE;
+ return INSANITY_SANE;
if (strncmp(insanity, "eccentric", MAX_ARG_LEN + 1) == 0)
- return TM_ECCENTRIC;
+ return INSANITY_ECCENTRIC;
if (strncmp(insanity, "manic", MAX_ARG_LEN + 1) == 0)
- return TM_MANIC;
+ return INSANITY_MANIC;
if (strncmp(insanity, "paranoid", MAX_ARG_LEN + 1) == 0)
- return TM_PARANOID;
+ return INSANITY_PARANOID;
return -1;
}
return VERIFY_NO;
if (strncmp(verify, "meta", MAX_ARG_LEN + 1) == 0)
return VERIFY_META;
+ if (strncmp(verify, "full", MAX_ARG_LEN + 1) == 0)
+ return VERIFY_FULL;
return -1;
}
int read_pattern(char *pattern)
{
if (strncmp(pattern, "seq", MAX_ARG_LEN + 1) == 0)
- return IO_SEQ;
+ return PATTERN_SEQ;
if (strncmp(pattern, "rand", MAX_ARG_LEN + 1) == 0)
- return IO_RAND;
+ return PATTERN_RAND;
return -1;
}
-/*
- * 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)
-{
- struct tm_result res;
-
- //The format we expect is the following:
- //
- // |-s-|-ms-|-us-|-ns|
- //num = 123 456 789 012 . 000000000000
- res.s = num / pow(10,9);
- num = fmod(num, pow(10,9));
- res.ms = num / pow(10,6);
- num = fmod(num, pow(10,6));
- res.us = num / 1000;
- res.ns = fmod(num, 1000);
-
- return res;
-}
+/*******************\
+ * Print functions *
+\*******************/
void print_stats(struct bench *prefs)
{
uint64_t remaining;
printf("\n");
- printf("Requests total: %10lu\n", prefs->max_requests);
- printf("Requests submitted: %10lu\n", prefs->sub_tm->completed);
- printf("Requests received: %10lu\n", prefs->rec_tm->completed);
+ 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);
+ if ((prefs->op == X_READ) && (GET_FLAG(VERIFY, prefs->flags) != VERIFY_NO))
+ printf("Requests corrupted: %10lu\n", prefs->status->corrupted);
printf("\n");
- remaining = prefs->max_requests - prefs->rec_tm->completed;
+ remaining = prefs->status->max - prefs->status->received;
if (remaining)
printf("Requests remaining: %10lu\n", remaining);
else
printf("Total time: %3u. %03u %03u %03u\n",
res.s, res.ms, res.us, res.ns);
- if (!prefs->rec_tm->completed)
+ if (!prefs->status->received)
return;
- res = separate_by_order(sum / prefs->rec_tm->completed);
+ res = separate_by_order(sum / prefs->status->received);
printf("Mean Time: %3u. %03u %03u %03u\n",
res.s, res.ms, res.us, res.ns);
//TODO: Add std
}
+/**************************\
+ * Benchmarking functions *
+\**************************/
+
void create_id(unsigned long seed)
{
if (seed > pow(10, 9))
struct bench_lfsr id_lfsr, obj_lfsr, off_lfsr;
uint64_t i;
+uint64_t determine_next(struct bench *prefs)
+{
+ if (GET_FLAG(PATTERN, prefs->flags) == PATTERN_SEQ)
+ return prefs->status->submitted;
+ else
+ return lfsr_next(prefs->lfsr);
+}
id = _get_id();
object = _get_object(prefs, new);
/* check for left-overs chunk */
}
-uint64_t determine_next(struct bench *prefs)
+int read_chunk(struct bench *prefs, struct xseg_request *req)
{
if ((prefs->flags & (1 << PATTERN_FLAG)) == IO_SEQ)
return prefs->sub_tm->completed;
#include <limits.h>
char global_id[IDLEN];
-
/*
* This macro checks two things:
* a) If in-flight requests are less than given iodepth
* b) If we have submitted all of the requests
*/
-#define CAN_SEND_REQUEST(prefs) \
- prefs->sub_tm->completed - prefs->rec_tm->completed < prefs->iodepth && \
- prefs->sub_tm->completed < prefs->max_requests \
+#define CAN_SEND_REQUEST(prefs) \
+ ((prefs->status->submitted - prefs->status->received < prefs->iodepth) && \
+ (prefs->status->submitted < prefs->status->max))
+
+#define CAN_VERIFY(prefs) \
+ ((GET_FLAG(VERIFY, prefs->flags) != VERIFY_NO) && prefs->op == X_READ)
void custom_peer_usage()
{
}
prefs->flags = 0;
+ prefs->status = malloc(sizeof(struct req_status));
+ if (!prefs->status) {
+ perror("malloc");
+ return -1;
+ }
+ memset(prefs->status, 0, sizeof(struct req_status));
+
//Begin reading the benchmark-specific arguments
BEGIN_READ_ARGS(argc, argv);
READ_ARG_STRING("-op", op, MAX_ARG_LEN);
XSEGLOG2(&lc, E, "Invalid syntax: --pattern %s\n", pattern);
goto arg_fail;
}
- prefs->flags |= (uint8_t)r;
+ SET_FLAG(PATTERN, prefs->flags, r);
if (!verify[0])
strcpy(verify, "no");
XSEGLOG2(&lc, E, "Invalid syntax: --verify %s\n", verify);
goto arg_fail;
}
+ SET_FLAG(VERIFY, prefs->flags, r);
//Default iodepth value is 1
if (iodepth < 0)
if (!insanity[0])
strcpy(insanity, "sane");
- prefs->insanity = read_insanity(insanity);
- if (prefs->insanity < 0) {
+ r = read_insanity(insanity);
+ if (r < 0) {
XSEGLOG2(&lc, E, "Invalid syntax: --insanity %s\n", insanity);
goto arg_fail;
}
+ SET_FLAG(INSANITY, prefs->flags, r);
/*
* If we have a request other than read/write, we don't need to check
//In this case, the maximum number of requests is the total number of
//objects we will handle
- prefs->max_requests = prefs->to;
+ prefs->status->max = prefs->to;
} else {
/*************************\
//In this case, the maximum number of requests is the number of blocks
//we need to cover the total I/O size
- prefs->max_requests = prefs->ts / prefs->bs;
+ prefs->status->max = prefs->ts / prefs->bs;
}
/*************************\
* Create timers for all metrics *
\*********************************/
- if (init_timer(&prefs->total_tm, TM_SANE))
+ if (init_timer(&prefs->total_tm, INSANITY_SANE))
goto tm_fail;
- if (init_timer(&prefs->sub_tm, TM_MANIC))
+ if (init_timer(&prefs->sub_tm, INSANITY_MANIC))
goto tm_fail;
- if (init_timer(&prefs->get_tm, TM_PARANOID))
+ if (init_timer(&prefs->get_tm, INSANITY_PARANOID))
goto tm_fail;
- if (init_timer(&prefs->rec_tm, TM_ECCENTRIC))
+ if (init_timer(&prefs->rec_tm, INSANITY_ECCENTRIC))
goto tm_fail;
/********************************\
}
create_id(seed);
- if ((prefs->flags & (1 << PATTERN_FLAG)) == IO_RAND) {
+ if (GET_FLAG(PATTERN, prefs->flags) == PATTERN_RAND) {
prefs->lfsr = malloc(sizeof(struct bench_lfsr));
if (!prefs->lfsr) {
perror("malloc");
goto lfsr_fail;
}
- r = lfsr_init(prefs->lfsr, prefs->max_requests, seed, seed & 0xF);
+ r = lfsr_init(prefs->lfsr, prefs->status->max, seed, seed & 0xF);
if (r && set_by_hand) {
XSEGLOG2(&lc, E, "LFSR could not be initialized\n");
goto lfsr_fail;
}
XSEGLOG2(&lc, I, "Global ID is %s\n", global_id);
- peer->peerd_loop = custom_peerd_loop;
+ peer->peerd_loop = bench_peerd_loop;
peer->priv = (void *) prefs;
return 0;
//Determine what the next target/chunk will be, based on I/O pattern
new = determine_next(prefs);
+ req->op = prefs->op;
XSEGLOG2(&lc, I, "Our new request is %lu\n", new);
//Create a target of this format: "bench-<global_id>-<obj_no>"
create_target(prefs, req, new);
create_chunk(prefs, req, new);
}
- req->op = prefs->op;
//Measure this?
XSEGLOG2(&lc, D, "Allocate peer request\n");
* QUESTION: Is this the fastest way?
*/
timer_start(prefs, prefs->rec_tm);
- if (prefs->rec_tm->insanity <= prefs->insanity)
+ if (prefs->rec_tm->insanity <= GET_FLAG(INSANITY, prefs->flags))
memcpy(pr->priv, &prefs->rec_tm->start_time, sizeof(struct timespec));
//Submit the request from the source port to the target port
XSEGLOG2(&lc, W, "Cannot submit request\n");
goto put_peer_request;
}
+ prefs->status->submitted++;
timer_stop(prefs, prefs->sub_tm, NULL);
//Send SIGIO to the process that has bound this port to inform that
* This function substitutes the default generic_peerd_loop of peer.c.
* It's plugged to struct peerd at custom peer's initialisation
*/
-int custom_peerd_loop(void *arg)
+int bench_peerd_loop(void *arg)
{
#ifdef MT
struct thread *t = (struct thread *) arg;
while (CAN_SEND_REQUEST(prefs)) {
xseg_cancel_wait(xseg, peer->portno_start);
XSEGLOG2(&lc, D, "...because %lu < %lu && %lu < %lu\n",
- prefs->sub_tm->completed - prefs->rec_tm->completed,
- prefs->iodepth, prefs->sub_tm->completed,
- prefs->max_requests);
+ 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 (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 (prefs->rec_tm->completed < prefs->max_requests)
+ if (prefs->status->received < prefs->status->max)
goto send_request;
else
return 0;
struct bench *prefs = peer->priv;
struct timer *rec = prefs->rec_tm;
+ prefs->status->received++;
if (!pr->req) {
//This is a serious error, so we must stop
XSEGLOG2(&lc, E, "Received peer request with no xseg request");
return;
}
- if (!pr->priv) {
+ if ((GET_FLAG(INSANITY, prefs->flags) < rec->insanity) && !pr->priv) {
XSEGLOG2(&lc, W, "Cannot find submission time of request");
return;
}
#define MAX_ARG_LEN 10
-#define TM_SANE 0
-#define TM_ECCENTRIC 1
-#define TM_MANIC 2
-#define TM_PARANOID 3
+/*
+ * Pattern type occupies 1st flag bit.
+ * If 1, it's sequential, if 0, it's random.
+ */
+#define PATTERN_FLAG_POS 0
+#define PATTERN_BITMASK 1
+#define PATTERN_SEQ 0
+#define PATTERN_RAND 1
/*
- * Pattern type occupies first flag bit.
- * If 1, it's synchronous, if 0, it's random.
+ * Verify mode occupies 2nd and 3rd flag bit.
+ * If 01, it uses metadata for verification, if 11 it writes pseudorandom nums
+ * in chunk's memory range and if 00, it's off.
*/
-#define PATTERN_FLAG 0
-#define IO_SEQ 0 << PATTERN_FLAG
-#define IO_RAND 1 << PATTERN_FLAG
+#define VERIFY_FLAG_POS 1
+#define VERIFY_BITMASK 3 /* i.e. "11" in binary form */
+#define VERIFY_NO 0
+#define VERIFY_META 1
+#define VERIFY_FULL 2
+
+/* Timer insanity occupies 4th and 5th flag bit */
+#define INSANITY_FLAG_POS 3
+#define INSANITY_BITMASK 3 /* i.e. "11" in binary form */
+#define INSANITY_SANE 0
+#define INSANITY_ECCENTRIC 1
+#define INSANITY_MANIC 2
+#define INSANITY_PARANOID 3
+
/*
- * Verify mode occupies second flag bit.
- * If 1, it uses metadata for verification, if 0, it's off.
+ * Current bench flags representation:
+ * 64 7 6 5 4 3 2 1 : bits
+ * ...0 0 0 0 0 0 0
+ * |____||____||_|
+ * ^ ^ ^
+ * | | |
+ * insanity | pattern
+ * verify
*/
-#define VERIFY_FLAG 1
-#define VERIFY_NO 0 << VERIFY_FLAG
-#define VERIFY_META 1 << VERIFY_FLAG
+/* Add flag bit according to its position */
+#define SET_FLAG(__ftype, __flag, __val) \
+ __flag |= __val << __ftype##_FLAG_POS;
+/* Apply bitmask to flags, shift result to the right to get correct value */
+#define GET_FLAG(__ftype, __flag) \
+ (__flag & (__ftype##_BITMASK << __ftype##_FLAG_POS)) >> __ftype##_FLAG_POS
/*
* The benchark ID (IDLEN) is global for the test, calculated once and is a
* string of the following form: {"bench-" + 9-digit number + "\0"}.
uint64_t ts; //Total I/O size
uint64_t os; //Object size
uint64_t bs; //Block size
- uint64_t max_requests; //Max number of requests for a benchmark
uint32_t iodepth; //Num of in-flight xseg reqs
- int insanity;
xport dst_port;
xport src_port;
uint32_t op; //xseg operation
- uint8_t flags;
+ uint64_t flags;
struct peerd *peer;
+ struct req_status *status;
struct bench_lfsr *lfsr;
struct timer *total_tm; //Total time for benchmark
struct timer *get_tm; //Time for xseg_get_request
struct timer *rec_tm; //Time for xseg_receive_request
};
+struct req_status {
+ uint64_t max; /* Max requests for benchmark */
+ uint64_t submitted;
+ uint64_t received;
+ uint64_t corrupted; /* Requests that did not pass verification */
+ uint64_t failed;
+};
+
/*
* 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
unsigned int ns;
};
-/* FILLME */
struct signature {
- char obj_name[TARGETLEN];
+ uint64_t id;
+ uint64_t object;
uint64_t offset;
- uint64_t size;
-
- //hash of data (heavy)
};
-int custom_peerd_loop(void *arg);
+int bench_peerd_loop(void *arg);
void timer_start(struct bench *prefs, struct timer *sample_req);
void timer_stop(struct bench *prefs, struct timer *sample_tm,