public inbox for fio@vger.kernel.org
 help / color / mirror / Atom feed
From: Jan Kara <jack@suse.cz>
To: Vincent Fu <vincentfu@gmail.com>
Cc: Jens Axboe <axboe@kernel.dk>,
	fio@vger.kernel.org, Jan Kara <jack@suse.cz>,
	Damien Le Moal <dlemoal@kernel.org>
Subject: [PATCH v2 5/5] Add option to specify ramp period by amount of IO
Date: Fri, 19 Dec 2025 14:42:39 +0100	[thread overview]
Message-ID: <20251219134247.14195-5-jack@suse.cz> (raw)
In-Reply-To: <20251219133853.8548-1-jack@suse.cz>

In some cases the ramp up period is not easy to define by amount of
time. This is for example a case of buffered writes measurement where we
want to start measuring only once dirty throttling kicks in. The time
until dirty throttling kicks in depends on dirty limit (easy to figure
out) and speed of writes to the page cache (difficult to know in
advance). Add option ramp_size which determines the ramp up period by
the amount of IO written (either by each job or by each group when group
reporting is enabled).

Reviewed-by: Damien Le Moal <dlemoal@kernel.org>
Signed-off-by: Jan Kara <jack@suse.cz>
---
 HOWTO.rst        | 10 ++++++++
 cconv.c          |  2 ++
 fio.1            |  9 +++++++
 fio_time.h       |  2 +-
 init.c           |  5 ++--
 options.c        | 10 ++++++++
 server.h         |  2 +-
 thread_options.h |  2 ++
 time.c           | 65 +++++++++++++++++++++++++++++++++++++++++++++---
 9 files changed, 100 insertions(+), 7 deletions(-)

diff --git a/HOWTO.rst b/HOWTO.rst
index 9f55a73bde05..af01f8f626f3 100644
--- a/HOWTO.rst
+++ b/HOWTO.rst
@@ -715,6 +715,16 @@ Time related parameters
 	:option:`runtime` is specified.  When the unit is omitted, the value is
 	given in seconds.
 
+.. option:: ramp_size=size
+
+	If set, fio will wait until the job does given amount of IO before
+	logging any performance numbers. When ``group_reporting`` is enabled,
+	the logging starts when all jobs in the group together perform given
+	amount of IO. Similarly to ``ramp_time`` this is useful for letting
+	performance to settle before logging results and will increase the total
+	runtime if a special timeout or :option:`runtime` is specified. When
+	the unit is omitted, the value is given in bytes.
+
 .. option:: clocksource=str
 
 	Use the given clocksource as the base of timing. The supported options are:
diff --git a/cconv.c b/cconv.c
index e7bbfc53bc64..0c4a3f2d21e6 100644
--- a/cconv.c
+++ b/cconv.c
@@ -258,6 +258,7 @@ int convert_thread_options_to_cpu(struct thread_options *o,
 	o->start_delay_high = le64_to_cpu(top->start_delay_high);
 	o->timeout = le64_to_cpu(top->timeout);
 	o->ramp_time = le64_to_cpu(top->ramp_time);
+	o->ramp_size = le64_to_cpu(top->ramp_size);
 	o->ss_dur = le64_to_cpu(top->ss_dur);
 	o->ss_ramp_time = le64_to_cpu(top->ss_ramp_time);
 	o->ss_state = le32_to_cpu(top->ss_state);
@@ -636,6 +637,7 @@ void convert_thread_options_to_net(struct thread_options_pack *top,
 	top->start_delay_high = __cpu_to_le64(o->start_delay_high);
 	top->timeout = __cpu_to_le64(o->timeout);
 	top->ramp_time = __cpu_to_le64(o->ramp_time);
+	top->ramp_size = __cpu_to_le64(o->ramp_size);
 	top->ss_dur = __cpu_to_le64(top->ss_dur);
 	top->ss_ramp_time = __cpu_to_le64(top->ss_ramp_time);
 	top->ss_state = cpu_to_le32(top->ss_state);
diff --git a/fio.1 b/fio.1
index 9c4ff08c86ad..3ee154ed98f1 100644
--- a/fio.1
+++ b/fio.1
@@ -497,6 +497,15 @@ thus it will increase the total runtime if a special timeout or
 \fBruntime\fR is specified. When the unit is omitted, the value is
 given in seconds.
 .TP
+.BI ramp_size \fR=\fPsize
+If set, fio will wait until the job does given amount of IO before
+logging any performance numbers. When \fBgroup_reporting\fR is enabled,
+the logging starts when all jobs in the group together perform given
+amount of IO. Similarly to \fBramp_time\fR this is useful for letting
+performance to settle before logging results and will increase the total
+runtime if a special timeout or \fBruntime\fR is specified. When
+the unit is omitted, the value is given in bytes.
+.TP
 .BI clocksource \fR=\fPstr
 Use the given clocksource as the base of timing. The supported options are:
 .RS
diff --git a/fio_time.h b/fio_time.h
index 54dad8c7812e..ef107c50dc50 100644
--- a/fio_time.h
+++ b/fio_time.h
@@ -34,7 +34,7 @@ extern void set_genesis_time(void);
 extern int ramp_period_check(void);
 extern bool ramp_period_over(struct thread_data *);
 extern bool in_ramp_period(struct thread_data *);
-extern void td_ramp_period_init(struct thread_data *);
+extern int td_ramp_period_init(struct thread_data *);
 extern void fio_time_init(void);
 extern void timespec_add_msec(struct timespec *, unsigned int);
 extern void set_epoch_time(struct thread_data *, clockid_t, clockid_t);
diff --git a/init.c b/init.c
index 8b7728907c7f..7c5124fc9a6c 100644
--- a/init.c
+++ b/init.c
@@ -1676,8 +1676,6 @@ static int add_job(struct thread_data *td, const char *jobname, int job_add_num,
 
 	init_thread_stat_min_vals(&td->ts);
 
-	td_ramp_period_init(td);
-
 	/*
 	 * td->>ddir_seq_nr needs to be initialized to 1, NOT o->ddir_seq_nr,
 	 * so that get_next_offset gets a new random offset the first time it
@@ -1705,6 +1703,9 @@ static int add_job(struct thread_data *td, const char *jobname, int job_add_num,
 	if (setup_rate(td))
 		goto err;
 
+	if (td_ramp_period_init(td))
+		goto err;
+
 	if (o->write_lat_log) {
 		struct log_params p = {
 			.td = td,
diff --git a/options.c b/options.c
index 8e3de528bbbb..62beb69266f0 100644
--- a/options.c
+++ b/options.c
@@ -3093,6 +3093,16 @@ struct fio_option fio_options[FIO_MAX_OPTS] = {
 		.category = FIO_OPT_C_GENERAL,
 		.group	= FIO_OPT_G_RUNTIME,
 	},
+	{
+		.name	= "ramp_size",
+		.lname	= "Ramp size",
+		.type	= FIO_OPT_STR_VAL,
+		.off1	= offsetof(struct thread_options, ramp_size),
+		.minval = 1,
+		.help	= "Amount of data transferred before measuring performance",
+		.category = FIO_OPT_C_GENERAL,
+		.group	= FIO_OPT_G_RUNTIME,
+	},
 	{
 		.name	= "clocksource",
 		.lname	= "Clock source",
diff --git a/server.h b/server.h
index 139f84b1c319..09e6663e4dde 100644
--- a/server.h
+++ b/server.h
@@ -51,7 +51,7 @@ struct fio_net_cmd_reply {
 };
 
 enum {
-	FIO_SERVER_VER			= 114,
+	FIO_SERVER_VER			= 116,
 
 	FIO_SERVER_MAX_FRAGMENT_PDU	= 1024,
 	FIO_SERVER_MAX_CMD_MB		= 2048,
diff --git a/thread_options.h b/thread_options.h
index 3abce7318ce2..b4dd8d7acd49 100644
--- a/thread_options.h
+++ b/thread_options.h
@@ -212,6 +212,7 @@ struct thread_options {
 	unsigned long long start_delay_high;
 	unsigned long long timeout;
 	unsigned long long ramp_time;
+	unsigned long long ramp_size;
 	unsigned int ss_state;
 	fio_fp64_t ss_limit;
 	unsigned long long ss_dur;
@@ -546,6 +547,7 @@ struct thread_options_pack {
 	uint64_t start_delay_high;
 	uint64_t timeout;
 	uint64_t ramp_time;
+	uint64_t ramp_size;
 	uint64_t ss_dur;
 	uint64_t ss_ramp_time;
 	uint32_t ss_state;
diff --git a/time.c b/time.c
index 2709d5b9784a..386c76fc31d0 100644
--- a/time.c
+++ b/time.c
@@ -125,11 +125,57 @@ bool ramp_period_enabled = false;
 
 int ramp_period_check(void)
 {
+	uint64_t group_bytes = 0;
+	int prev_groupid = -1;
+	bool group_ramp_period_over = false;
+
 	for_each_td(td) {
 		if (td->ramp_period_state != RAMP_RUNNING)
 			continue;
-		if (utime_since_now(&td->epoch) >= td->o.ramp_time)
+
+		if (td->o.ramp_time &&
+		    utime_since_now(&td->epoch) >= td->o.ramp_time) {
 			td->ramp_period_state = RAMP_FINISHING;
+			continue;
+		}
+
+		if (td->o.ramp_size) {
+			int ddir;
+			const bool needs_lock = td_async_processing(td);
+
+			if (!td->o.group_reporting ||
+			    (td->o.group_reporting &&
+			     td->groupid != prev_groupid)) {
+				group_bytes = 0;
+				prev_groupid = td->groupid;
+				group_ramp_period_over = false;
+			}
+
+			if (needs_lock)
+				__td_io_u_lock(td);
+
+			for (ddir = 0; ddir < DDIR_RWDIR_CNT; ddir++)
+				group_bytes += td->io_bytes[ddir];
+
+			if (needs_lock)
+				__td_io_u_unlock(td);
+
+			if (group_bytes >= td->o.ramp_size) {
+				td->ramp_period_state = RAMP_FINISHING;
+				/*
+				 * Mark ramp up for all threads in the group as
+				 * done.
+				 */
+				if (td->o.group_reporting &&
+				    !group_ramp_period_over) {
+					group_ramp_period_over = true;
+					for_each_td(td2) {
+						if (td2->groupid == td->groupid)
+							 td2->ramp_period_state = RAMP_FINISHING;
+					} end_for_each();
+				}
+			}
+		}
 	} end_for_each();
 
 	return 0;
@@ -173,14 +219,27 @@ bool ramp_period_over(struct thread_data *td)
 	return true;
 }
 
-void td_ramp_period_init(struct thread_data *td)
+int td_ramp_period_init(struct thread_data *td)
 {
-	if (td->o.ramp_time) {
+	if (td->o.ramp_time || td->o.ramp_size) {
+		if (td->o.ramp_time && td->o.ramp_size) {
+			td_verror(td, EINVAL, "job rejected: cannot specify both ramp_time and ramp_size");
+			return 1;
+		}
+		/* Make sure options are consistent within reporting group */
+		for_each_td(td2) {
+			if (td->groupid == td2->groupid &&
+			    td->o.ramp_size != td2->o.ramp_size) {
+				td_verror(td, EINVAL, "job rejected: inconsistent ramp_size within reporting group");
+				return 1;
+			}
+		} end_for_each();
 		td->ramp_period_state = RAMP_RUNNING;
 		ramp_period_enabled = true;
 	} else {
 		td->ramp_period_state = RAMP_DONE;
 	}
+	return 0;
 }
 
 void fio_time_init(void)
-- 
2.51.0


  parent reply	other threads:[~2025-12-19 13:43 UTC|newest]

Thread overview: 9+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2025-12-19 13:42 [PATCH v2 0/5] Add support for specifying ramp up period by amount of IO Jan Kara
2025-12-19 13:42 ` [PATCH v2 1/5] time: rename in_ramp_time() and ramp_time_over() Jan Kara
2025-12-19 13:42 ` [PATCH v2 2/5] td: Initialize ramp_period_over based on options Jan Kara
2025-12-19 13:42 ` [PATCH v2 3/5] eta: Use in_ramp_period() instead of opencoding it Jan Kara
2025-12-19 13:42 ` [PATCH v2 4/5] time: Evaluate ramp up condition once per second Jan Kara
2025-12-19 13:42 ` Jan Kara [this message]
2025-12-24 19:44 ` [PATCH v2 0/5] Add support for specifying ramp up period by amount of IO Vincent Fu
2026-01-07 17:02   ` Jan Kara
2026-01-07 21:26     ` Vincent Fu

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20251219134247.14195-5-jack@suse.cz \
    --to=jack@suse.cz \
    --cc=axboe@kernel.dk \
    --cc=dlemoal@kernel.org \
    --cc=fio@vger.kernel.org \
    --cc=vincentfu@gmail.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox