Add ramp_time option

Sometimes it's useful to let a job settle for a little while
before taking any measurements on latency and throughput, since
the initial rate on eg a write workload may be much higher than
the longer sustained rate.

So add a ramp_time option that allows the user to specify a lead
ramp time that must have passed before fio takes any performance
numbers into account.

Suggested by "Jenkins, Lee" <Lee.Jenkins@hp.com>

Signed-off-by: Jens Axboe <jens.axboe@oracle.com>
diff --git a/HOWTO b/HOWTO
index 7a65aa1..dd2f84a 100644
--- a/HOWTO
+++ b/HOWTO
@@ -574,6 +574,11 @@
 		written. It will simply loop over the same workload
 		as many times as the runtime allows.
 
+ramp_time	If set, fio will run the specified workload for this amount
+		of time before logging any performance numbers. Useful for
+		letting performance settle before logging results, thus
+		minimizing the runtime required for stable results.
+
 invalidate=bool	Invalidate the buffer/page cache parts for this file prior
 		to starting io. Defaults to true.
 
diff --git a/fio.c b/fio.c
index ee752e1..2a09fbe 100644
--- a/fio.c
+++ b/fio.c
@@ -619,15 +619,17 @@
 		 * of completions except the very first one which may look
 		 * a little bursty
 		 */
-		usec = utime_since(&s, &comp_time);
+		if (ramp_time_over(td)) {
+			usec = utime_since(&s, &comp_time);
 
-		rate_throttle(td, usec, bytes_done);
+			rate_throttle(td, usec, bytes_done);
 
-		if (check_min_rate(td, &comp_time)) {
-			if (exitall_on_terminate)
-				terminate_threads(td->groupid);
-			td_verror(td, EIO, "check_min_rate");
-			break;
+			if (check_min_rate(td, &comp_time)) {
+				if (exitall_on_terminate)
+					terminate_threads(td->groupid);
+				td_verror(td, EIO, "check_min_rate");
+				break;
+			}
 		}
 
 		if (td->o.thinktime) {
diff --git a/fio.h b/fio.h
index 22aaf34..a0c1109 100644
--- a/fio.h
+++ b/fio.h
@@ -473,6 +473,7 @@
 	unsigned int fsync_blocks;
 	unsigned int start_delay;
 	unsigned long long timeout;
+	unsigned long long ramp_time;
 	unsigned int overwrite;
 	unsigned int bw_avg_time;
 	unsigned int loops;
@@ -609,6 +610,7 @@
 	struct timeval rw_end[2];
 	struct timeval last_issue;
 	unsigned int rw_end_set[2];
+	unsigned int ramp_time_over;
 
 	/*
 	 * read/write mixed workload state
@@ -817,6 +819,7 @@
 extern void fill_start_time(struct timeval *);
 extern void fio_gettime(struct timeval *, void *);
 extern void set_genesis_time(void);
+extern int ramp_time_over(struct thread_data *);
 
 /*
  * Init/option functions
diff --git a/io_u.c b/io_u.c
index 1dff88f..17db16b 100644
--- a/io_u.c
+++ b/io_u.c
@@ -903,24 +903,29 @@
 	if (!io_u->error) {
 		unsigned int bytes = io_u->buflen - io_u->resid;
 		const enum fio_ddir idx = io_u->ddir;
-		int ret;
+		int ret, ramp_done;
 
-		td->io_blocks[idx]++;
-		td->io_bytes[idx] += bytes;
-		td->this_io_bytes[idx] += bytes;
+		ramp_done = ramp_time_over(td);
 
-		usec = utime_since(&io_u->issue_time, &icd->time);
+		if (ramp_done) {
+			td->io_blocks[idx]++;
+			td->io_bytes[idx] += bytes;
+			td->this_io_bytes[idx] += bytes;
 
-		add_clat_sample(td, idx, usec);
-		add_bw_sample(td, idx, &icd->time);
-		io_u_mark_latency(td, usec);
+			usec = utime_since(&io_u->issue_time, &icd->time);
+
+			add_clat_sample(td, idx, usec);
+			add_bw_sample(td, idx, &icd->time);
+			io_u_mark_latency(td, usec);
+		}
 
 		if (td_write(td) && idx == DDIR_WRITE &&
 		    td->o.do_verify &&
 		    td->o.verify != VERIFY_NONE)
 			log_io_piece(td, io_u);
 
-		icd->bytes_done[idx] += bytes;
+		if (ramp_done)
+			icd->bytes_done[idx] += bytes;
 
 		if (io_u->end_io) {
 			ret = io_u->end_io(td, io_u);
diff --git a/options.c b/options.c
index 0608e7b..8723adf 100644
--- a/options.c
+++ b/options.c
@@ -832,6 +832,12 @@
 		.help	= "Keep running until runtime/timeout is met",
 	},
 	{
+		.name	= "ramp_time",
+		.type	= FIO_OPT_STR_VAL_TIME,
+		.off1	= td_var_offset(ramp_time),
+		.help	= "Ramp up time before measuring performance",
+	},
+	{
 		.name	= "mem",
 		.alias	= "iomem",
 		.type	= FIO_OPT_STR,
diff --git a/time.c b/time.c
index 505058f..4f1c13a 100644
--- a/time.c
+++ b/time.c
@@ -164,6 +164,23 @@
 	return mtime_since_now(&genesis);
 }
 
+int ramp_time_over(struct thread_data *td)
+{
+	struct timeval tv;
+
+	if (!td->o.ramp_time || td->ramp_time_over)
+		return 1;
+
+	fio_gettime(&tv, NULL);
+	if (mtime_since(&td->epoch, &tv) >= td->o.ramp_time * 1000) {
+		td->ramp_time_over = 1;
+		memcpy(&td->start, &tv, sizeof(tv));
+		return 1;
+	}
+
+	return 0;
+}
+
 static void fio_init time_init(void)
 {
 	int i;