fio: Introduce new constant thinkcycles option
authorChristian Loehle <christian.loehle@arm.com>
Mon, 23 Oct 2023 09:42:26 +0000 (10:42 +0100)
committerChristian Loehle <christian.loehle@arm.com>
Fri, 3 Nov 2023 08:51:02 +0000 (08:51 +0000)
The thinkcycles parameter allows to set a number of cycles to spin between
requests to model real-world applications more realistically

The thinktime parameter family can be used to model an application processing
the data to be able to model real-world applications more closely.
Unfortunately this is currently set per constant time and therefore is affected
by CPU frequency settings or task migration to a CPU with different capacity.
The new thinkcycles parameter closes that gap and allows specifying a constant
number of cycles instead, such that CPU capacity is taken into account.

Signed-off-by: Christian Loehle <christian.loehle@arm.com>
HOWTO.rst
backend.c
cconv.c
fio.1
fio_time.h
options.c
thread_options.h
time.c

index 34d6afdf6e6e4f87df0f5db29f1a8df34ba32207..42b2b119741ab9ab4c1b119cefc1fe065fbeab60 100644 (file)
--- a/HOWTO.rst
+++ b/HOWTO.rst
@@ -3209,6 +3209,14 @@ I/O depth
 I/O rate
 ~~~~~~~~
 
+.. option:: thinkcycles=int
+
+       Stall the job for the specified number of cycles after an I/O has completed before
+       issuing the next. May be used to simulate processing being done by an application.
+       This is not taken into account for the time to be waited on for  :option:`thinktime`.
+       Might not have any effect on some platforms, this can be checked by trying a setting
+       a high enough amount of thinkcycles.
+
 .. option:: thinktime=time
 
        Stall the job for the specified period of time after an I/O has completed before issuing the
index a5895fec4d5d44096d1c67145b65a82180c100b0..1fab467a1b06083d9428a68ee906d9e7e5b21500 100644 (file)
--- a/backend.c
+++ b/backend.c
@@ -49,6 +49,7 @@
 #include "helper_thread.h"
 #include "pshared.h"
 #include "zone-dist.h"
+#include "fio_time.h"
 
 static struct fio_sem *startup_sem;
 static struct flist_head *cgroup_list;
@@ -1133,6 +1134,9 @@ reap:
                if (ret < 0)
                        break;
 
+               if (ddir_rw(ddir) && td->o.thinkcycles)
+                       cycles_spin(td->o.thinkcycles);
+
                if (ddir_rw(ddir) && td->o.thinktime)
                        handle_thinktime(td, ddir, &comp_time);
 
diff --git a/cconv.c b/cconv.c
index 341388d4568125c9bfc51bfb384b6e1909fecaf3..c92984081b15c42c3236c7d7056da8885d6379dc 100644 (file)
--- a/cconv.c
+++ b/cconv.c
@@ -233,6 +233,7 @@ int convert_thread_options_to_cpu(struct thread_options *o,
        o->random_generator = le32_to_cpu(top->random_generator);
        o->hugepage_size = le32_to_cpu(top->hugepage_size);
        o->rw_min_bs = le64_to_cpu(top->rw_min_bs);
+       o->thinkcycles = le32_to_cpu(top->thinkcycles);
        o->thinktime = le32_to_cpu(top->thinktime);
        o->thinktime_spin = le32_to_cpu(top->thinktime_spin);
        o->thinktime_blocks = le32_to_cpu(top->thinktime_blocks);
@@ -472,6 +473,7 @@ void convert_thread_options_to_net(struct thread_options_pack *top,
        top->random_generator = cpu_to_le32(o->random_generator);
        top->hugepage_size = cpu_to_le32(o->hugepage_size);
        top->rw_min_bs = __cpu_to_le64(o->rw_min_bs);
+       top->thinkcycles = cpu_to_le32(o->thinkcycles);
        top->thinktime = cpu_to_le32(o->thinktime);
        top->thinktime_spin = cpu_to_le32(o->thinktime_spin);
        top->thinktime_blocks = cpu_to_le32(o->thinktime_blocks);
diff --git a/fio.1 b/fio.1
index c4742aa92536cee09dc6c988bef220ad3f3c2cf9..d62da6885e8063220636fe2059988de8eb155435 100644 (file)
--- a/fio.1
+++ b/fio.1
@@ -2962,6 +2962,13 @@ reporting if I/O gets backed up on the device side (the coordinated omission
 problem). Note that this option cannot reliably be used with async IO engines.
 .SS "I/O rate"
 .TP
+.BI thinkcycles \fR=\fPint
+Stall the job for the specified number of cycles after an I/O has completed before
+issuing the next. May be used to simulate processing being done by an application.
+This is not taken into account for the time to be waited on for \fBthinktime\fR.
+Might not have any effect on some platforms, this can be checked by trying a setting
+a high enough amount of thinkcycles.
+.TP
 .BI thinktime \fR=\fPtime
 Stall the job for the specified period of time after an I/O has completed before issuing the
 next. May be used to simulate processing being done by an application.
index b20e734c4a8a3053cd4262458575e670710a8e7c..969ad68d5b9cdd0116a79c53d157c4366e7011ad 100644 (file)
@@ -22,6 +22,7 @@ extern uint64_t time_since_now(const struct timespec *);
 extern uint64_t time_since_genesis(void);
 extern uint64_t mtime_since_genesis(void);
 extern uint64_t utime_since_genesis(void);
+extern void cycles_spin(unsigned int);
 extern uint64_t usec_spin(unsigned int);
 extern uint64_t usec_sleep(struct thread_data *, unsigned long);
 extern void fill_start_time(struct timespec *);
index 6b2cb53f77b381db1e9fa875ad73f96111ed124c..53df03de9eb5e51d21afffa5e0d4be4ee982918b 100644 (file)
--- a/options.c
+++ b/options.c
@@ -3875,6 +3875,18 @@ struct fio_option fio_options[FIO_MAX_OPTS] = {
                .category = FIO_OPT_C_IO,
                .group  = FIO_OPT_G_THINKTIME,
        },
+       {
+               .name   = "thinkcycles",
+               .lname  = "Think cycles",
+               .type   = FIO_OPT_INT,
+               .off1   = offsetof(struct thread_options, thinkcycles),
+               .help   = "Spin for a constant amount of cycles between requests",
+               .def    = "0",
+               .parent = "thinktime",
+               .hide   = 1,
+               .category = FIO_OPT_C_IO,
+               .group  = FIO_OPT_G_THINKTIME,
+       },
        {
                .name   = "thinktime_blocks",
                .lname  = "Thinktime blocks",
index fdde055e7d71932753b4b1fd500a510929f61693..24f695fe1a4d665b73c7800fadbcbe65354afcd9 100644 (file)
@@ -309,6 +309,8 @@ struct thread_options {
        char *exec_prerun;
        char *exec_postrun;
 
+       unsigned int thinkcycles;
+
        unsigned int thinktime;
        unsigned int thinktime_spin;
        unsigned int thinktime_blocks;
@@ -355,8 +357,8 @@ struct thread_options {
 
        unsigned long long latency_target;
        unsigned long long latency_window;
-       fio_fp64_t latency_percentile;
        uint32_t latency_run;
+       fio_fp64_t latency_percentile;
 
        /*
         * flow support
@@ -626,6 +628,8 @@ struct thread_options_pack {
        uint8_t exec_prerun[FIO_TOP_STR_MAX];
        uint8_t exec_postrun[FIO_TOP_STR_MAX];
 
+       uint32_t thinkcycles;
+
        uint32_t thinktime;
        uint32_t thinktime_spin;
        uint32_t thinktime_blocks;
@@ -671,8 +675,8 @@ struct thread_options_pack {
        uint64_t latency_target;
        uint64_t latency_window;
        uint64_t max_latency[DDIR_RWDIR_CNT];
-       fio_fp64_t latency_percentile;
        uint32_t latency_run;
+       fio_fp64_t latency_percentile;
 
        /*
         * flow support
diff --git a/time.c b/time.c
index 7cbab6ff4f9ea1d029e7d9d2c0253278061ffe60..7f85c8de3bcbd2cb40eabe9c5f5c465551539b3a 100644 (file)
--- a/time.c
+++ b/time.c
@@ -38,6 +38,17 @@ uint64_t usec_spin(unsigned int usec)
        return t;
 }
 
+/*
+ * busy loop for a fixed amount of cycles
+ */
+void cycles_spin(unsigned int n)
+{
+       unsigned long i;
+
+       for (i=0; i < n; i++)
+               nop;
+}
+
 uint64_t usec_sleep(struct thread_data *td, unsigned long usec)
 {
        struct timespec req;