docs: update for new data placement options
[fio.git] / steadystate.c
CommitLineData
ba8fb6f6
VF
1#include <stdlib.h>
2
16e56d25
VF
3#include "fio.h"
4#include "steadystate.h"
16e56d25 5
84784e07 6bool steadystate_enabled = false;
90e678ba 7unsigned int ss_check_interval = 1000;
4cd5b75b 8
c27cc65f
JA
9void steadystate_free(struct thread_data *td)
10{
11 free(td->ss.iops_data);
12 free(td->ss.bw_data);
13 td->ss.iops_data = NULL;
14 td->ss.bw_data = NULL;
15}
16
f0c50c66
JA
17static void steadystate_alloc(struct thread_data *td)
18{
90e678ba
CL
19 int intervals = td->ss.dur / (ss_check_interval / 1000L);
20
21 td->ss.bw_data = calloc(intervals, sizeof(uint64_t));
22 td->ss.iops_data = calloc(intervals, sizeof(uint64_t));
bb49c8bd 23
c8caba48 24 td->ss.state |= FIO_SS_DATA;
f0c50c66
JA
25}
26
4cd5b75b 27void steadystate_setup(void)
16e56d25 28{
da8f124f
H
29 struct thread_data *prev_td;
30 int prev_groupid;
16e56d25 31
84784e07 32 if (!steadystate_enabled)
16e56d25
VF
33 return;
34
35 /*
36 * if group reporting is enabled, identify the last td
37 * for each group and use it for storing steady state
38 * data
39 */
40 prev_groupid = -1;
41 prev_td = NULL;
da8f124f 42 for_each_td(td) {
bb49c8bd 43 if (!td->ss.dur)
ba8fb6f6
VF
44 continue;
45
46 if (!td->o.group_reporting) {
47 steadystate_alloc(td);
16e56d25 48 continue;
ba8fb6f6 49 }
16e56d25
VF
50
51 if (prev_groupid != td->groupid) {
af7fb4aa 52 if (prev_td)
ba8fb6f6 53 steadystate_alloc(prev_td);
16e56d25
VF
54 prev_groupid = td->groupid;
55 }
56 prev_td = td;
da8f124f 57 } end_for_each();
16e56d25 58
af7fb4aa 59 if (prev_td && prev_td->o.group_reporting)
ba8fb6f6 60 steadystate_alloc(prev_td);
ba8fb6f6
VF
61}
62
9d193019 63static bool steadystate_slope(uint64_t iops, uint64_t bw,
4cd5b75b 64 struct thread_data *td)
16e56d25 65{
ba8fb6f6 66 int i, j;
16e56d25 67 double result;
16e56d25 68 struct steadystate_data *ss = &td->ss;
9d193019 69 uint64_t new_val;
90e678ba 70 int intervals = ss->dur / (ss_check_interval / 1000L);
16e56d25 71
ba8fb6f6
VF
72 ss->bw_data[ss->tail] = bw;
73 ss->iops_data[ss->tail] = iops;
16e56d25 74
c8caba48 75 if (ss->state & FIO_SS_IOPS)
7be78a8a
JA
76 new_val = iops;
77 else
78 new_val = bw;
79
90e678ba 80 if (ss->state & FIO_SS_BUFFER_FULL || ss->tail - ss->head == intervals - 1) {
c8caba48 81 if (!(ss->state & FIO_SS_BUFFER_FULL)) {
17df7023 82 /* first time through */
90e678ba 83 for (i = 0, ss->sum_y = 0; i < intervals; i++) {
c8caba48 84 if (ss->state & FIO_SS_IOPS)
7be78a8a
JA
85 ss->sum_y += ss->iops_data[i];
86 else
87 ss->sum_y += ss->bw_data[i];
93fdba01 88 j = (ss->head + i) % intervals;
c8caba48 89 if (ss->state & FIO_SS_IOPS)
94f218f6 90 ss->sum_xy += i * ss->iops_data[j];
7be78a8a 91 else
94f218f6 92 ss->sum_xy += i * ss->bw_data[j];
16e56d25 93 }
c8caba48 94 ss->state |= FIO_SS_BUFFER_FULL;
16e56d25
VF
95 } else { /* easy to update the sums */
96 ss->sum_y -= ss->oldest_y;
ba8fb6f6 97 ss->sum_y += new_val;
93fdba01 98 ss->sum_xy = ss->sum_xy - ss->sum_y + intervals * new_val;
16e56d25
VF
99 }
100
c8caba48 101 if (ss->state & FIO_SS_IOPS)
7be78a8a
JA
102 ss->oldest_y = ss->iops_data[ss->head];
103 else
104 ss->oldest_y = ss->bw_data[ss->head];
16e56d25
VF
105
106 /*
7be78a8a
JA
107 * calculate slope as (sum_xy - sum_x * sum_y / n) / (sum_(x^2)
108 * - (sum_x)^2 / n) This code assumes that all x values are
109 * equally spaced when they are often off by a few milliseconds.
110 * This assumption greatly simplifies the calculations.
16e56d25 111 */
93fdba01
VF
112 ss->slope = (ss->sum_xy - (double) ss->sum_x * ss->sum_y / intervals) /
113 (ss->sum_x_sq - (double) ss->sum_x * ss->sum_x / intervals);
c8caba48 114 if (ss->state & FIO_SS_PCT)
93fdba01 115 ss->criterion = 100.0 * ss->slope / (ss->sum_y / intervals);
7be78a8a
JA
116 else
117 ss->criterion = ss->slope;
16e56d25 118
7be78a8a
JA
119 dprint(FD_STEADYSTATE, "sum_y: %llu, sum_xy: %llu, slope: %f, "
120 "criterion: %f, limit: %f\n",
9d193019
VF
121 (unsigned long long) ss->sum_y,
122 (unsigned long long) ss->sum_xy,
123 ss->slope, ss->criterion, ss->limit);
16e56d25 124
6da94b07 125 result = ss->criterion * (ss->criterion < 0.0 ? -1.0 : 1.0);
16e56d25
VF
126 if (result < ss->limit)
127 return true;
128 }
129
90e678ba 130 ss->tail = (ss->tail + 1) % intervals;
16e56d25 131 if (ss->tail <= ss->head)
90e678ba 132 ss->head = (ss->head + 1) % intervals;
4cd5b75b 133
16e56d25
VF
134 return false;
135}
136
9d193019 137static bool steadystate_deviation(uint64_t iops, uint64_t bw,
4cd5b75b 138 struct thread_data *td)
16e56d25
VF
139{
140 int i;
141 double diff;
142 double mean;
16e56d25
VF
143
144 struct steadystate_data *ss = &td->ss;
90e678ba 145 int intervals = ss->dur / (ss_check_interval / 1000L);
16e56d25 146
ba8fb6f6
VF
147 ss->bw_data[ss->tail] = bw;
148 ss->iops_data[ss->tail] = iops;
16e56d25 149
90e678ba 150 if (ss->state & FIO_SS_BUFFER_FULL || ss->tail - ss->head == intervals - 1) {
c8caba48 151 if (!(ss->state & FIO_SS_BUFFER_FULL)) {
17df7023 152 /* first time through */
90e678ba 153 for (i = 0, ss->sum_y = 0; i < intervals; i++) {
c8caba48 154 if (ss->state & FIO_SS_IOPS)
7be78a8a
JA
155 ss->sum_y += ss->iops_data[i];
156 else
157 ss->sum_y += ss->bw_data[i];
90e678ba 158 }
c8caba48 159 ss->state |= FIO_SS_BUFFER_FULL;
16e56d25
VF
160 } else { /* easy to update the sum */
161 ss->sum_y -= ss->oldest_y;
c8caba48 162 if (ss->state & FIO_SS_IOPS)
7be78a8a
JA
163 ss->sum_y += ss->iops_data[ss->tail];
164 else
165 ss->sum_y += ss->bw_data[ss->tail];
16e56d25
VF
166 }
167
c8caba48 168 if (ss->state & FIO_SS_IOPS)
7be78a8a
JA
169 ss->oldest_y = ss->iops_data[ss->head];
170 else
171 ss->oldest_y = ss->bw_data[ss->head];
172
90e678ba 173 mean = (double) ss->sum_y / intervals;
ba8fb6f6 174 ss->deviation = 0.0;
16e56d25 175
90e678ba 176 for (i = 0; i < intervals; i++) {
c8caba48 177 if (ss->state & FIO_SS_IOPS)
7be78a8a
JA
178 diff = ss->iops_data[i] - mean;
179 else
180 diff = ss->bw_data[i] - mean;
6da94b07 181 ss->deviation = max(ss->deviation, diff * (diff < 0.0 ? -1.0 : 1.0));
16e56d25
VF
182 }
183
c8caba48 184 if (ss->state & FIO_SS_PCT)
7be78a8a
JA
185 ss->criterion = 100.0 * ss->deviation / mean;
186 else
187 ss->criterion = ss->deviation;
16e56d25 188
90e678ba 189 dprint(FD_STEADYSTATE, "intervals: %d, sum_y: %llu, mean: %f, max diff: %f, "
7be78a8a 190 "objective: %f, limit: %f\n",
90e678ba 191 intervals,
9d193019
VF
192 (unsigned long long) ss->sum_y, mean,
193 ss->deviation, ss->criterion, ss->limit);
16e56d25
VF
194
195 if (ss->criterion < ss->limit)
196 return true;
197 }
198
90e678ba
CL
199 ss->tail = (ss->tail + 1) % intervals;
200 if (ss->tail == ss->head)
201 ss->head = (ss->head + 1) % intervals;
4cd5b75b 202
16e56d25
VF
203 return false;
204}
4cd5b75b 205
966f8ef9 206int steadystate_check(void)
4cd5b75b 207{
da8f124f 208 int ddir, prev_groupid, group_ramp_time_over = 0;
4cd5b75b 209 unsigned long rate_time;
8b6a404c 210 struct timespec now;
9d193019
VF
211 uint64_t group_bw = 0, group_iops = 0;
212 uint64_t td_iops, td_bytes;
4cd5b75b
JA
213 bool ret;
214
215 prev_groupid = -1;
da8f124f 216 for_each_td(td) {
26b3a188 217 const bool needs_lock = td_async_processing(td);
4cd5b75b
JA
218 struct steadystate_data *ss = &td->ss;
219
7be78a8a 220 if (!ss->dur || td->runstate <= TD_SETTING_UP ||
0c13c969 221 td->runstate >= TD_EXITED || !ss->state ||
c8caba48 222 ss->state & FIO_SS_ATTAINED)
4cd5b75b
JA
223 continue;
224
225 td_iops = 0;
226 td_bytes = 0;
227 if (!td->o.group_reporting ||
228 (td->o.group_reporting && td->groupid != prev_groupid)) {
229 group_bw = 0;
230 group_iops = 0;
231 group_ramp_time_over = 0;
232 }
233 prev_groupid = td->groupid;
234
235 fio_gettime(&now, NULL);
c8caba48 236 if (ss->ramp_time && !(ss->state & FIO_SS_RAMP_OVER)) {
4cd5b75b 237 /*
90e678ba 238 * Begin recording data one check interval after ss->ramp_time
4cd5b75b
JA
239 * has elapsed
240 */
90e678ba 241 if (utime_since(&td->epoch, &now) >= (ss->ramp_time + ss_check_interval * 1000L))
c8caba48 242 ss->state |= FIO_SS_RAMP_OVER;
5b4b6586 243 }
4cd5b75b 244
26b3a188
JA
245 if (needs_lock)
246 __td_io_u_lock(td);
247
c1f50f76 248 for (ddir = 0; ddir < DDIR_RWDIR_CNT; ddir++) {
4cd5b75b
JA
249 td_iops += td->io_blocks[ddir];
250 td_bytes += td->io_bytes[ddir];
251 }
26b3a188
JA
252
253 if (needs_lock)
254 __td_io_u_unlock(td);
4cd5b75b
JA
255
256 rate_time = mtime_since(&ss->prev_time, &now);
257 memcpy(&ss->prev_time, &now, sizeof(now));
258
c8caba48 259 if (ss->state & FIO_SS_RAMP_OVER) {
90e678ba
CL
260 group_bw += rate_time * (td_bytes - ss->prev_bytes) /
261 (ss_check_interval * ss_check_interval / 1000L);
262 group_iops += rate_time * (td_iops - ss->prev_iops) /
263 (ss_check_interval * ss_check_interval / 1000L);
4cd5b75b
JA
264 ++group_ramp_time_over;
265 }
266 ss->prev_iops = td_iops;
267 ss->prev_bytes = td_bytes;
268
c8caba48 269 if (td->o.group_reporting && !(ss->state & FIO_SS_DATA))
4cd5b75b
JA
270 continue;
271
7be78a8a
JA
272 /*
273 * Don't begin checking criterion until ss->ramp_time is over
274 * for at least one thread in group
275 */
4cd5b75b
JA
276 if (!group_ramp_time_over)
277 continue;
278
7be78a8a
JA
279 dprint(FD_STEADYSTATE, "steadystate_check() thread: %d, "
280 "groupid: %u, rate_msec: %ld, "
9d193019 281 "iops: %llu, bw: %llu, head: %d, tail: %d\n",
da8f124f 282 __td_index, td->groupid, rate_time,
9d193019
VF
283 (unsigned long long) group_iops,
284 (unsigned long long) group_bw,
285 ss->head, ss->tail);
4cd5b75b 286
c8caba48 287 if (ss->state & FIO_SS_SLOPE)
4cd5b75b
JA
288 ret = steadystate_slope(group_iops, group_bw, td);
289 else
290 ret = steadystate_deviation(group_iops, group_bw, td);
291
292 if (ret) {
293 if (td->o.group_reporting) {
da8f124f 294 for_each_td(td2) {
4cd5b75b 295 if (td2->groupid == td->groupid) {
c8caba48 296 td2->ss.state |= FIO_SS_ATTAINED;
4cd5b75b
JA
297 fio_mark_td_terminate(td2);
298 }
da8f124f 299 } end_for_each();
4cd5b75b 300 } else {
c8caba48 301 ss->state |= FIO_SS_ATTAINED;
4cd5b75b
JA
302 fio_mark_td_terminate(td);
303 }
304 }
da8f124f 305 } end_for_each();
966f8ef9 306 return 0;
4cd5b75b
JA
307}
308
56a90eba 309int td_steadystate_init(struct thread_data *td)
f0c50c66
JA
310{
311 struct steadystate_data *ss = &td->ss;
312 struct thread_options *o = &td->o;
93fdba01 313 int intervals;
f0c50c66
JA
314
315 memset(ss, 0, sizeof(*ss));
316
56a90eba
VF
317 if (o->ss_dur) {
318 steadystate_enabled = true;
319 o->ss_dur /= 1000000L;
320
321 /* put all steady state info in one place */
322 ss->dur = o->ss_dur;
323 ss->limit = o->ss_limit.u.f;
324 ss->ramp_time = o->ss_ramp_time;
90e678ba 325 ss_check_interval = o->ss_check_interval / 1000L;
f0c50c66 326
2c5d94bc 327 ss->state = o->ss_state;
56a90eba 328 if (!td->ss.ramp_time)
c8caba48 329 ss->state |= FIO_SS_RAMP_OVER;
4cd5b75b 330
93fdba01
VF
331 intervals = ss->dur / (ss_check_interval / 1000L);
332 ss->sum_x = intervals * (intervals - 1) / 2;
333 ss->sum_x_sq = (intervals - 1) * (intervals) * (2*intervals - 1) / 6;
56a90eba 334 }
5b4b6586 335
56a90eba 336 /* make sure that ss options are consistent within reporting group */
da8f124f 337 for_each_td(td2) {
56a90eba
VF
338 if (td2->groupid == td->groupid) {
339 struct steadystate_data *ss2 = &td2->ss;
340
341 if (ss2->dur != ss->dur ||
342 ss2->limit != ss->limit ||
343 ss2->ramp_time != ss->ramp_time ||
56a90eba
VF
344 ss2->state != ss->state ||
345 ss2->sum_x != ss->sum_x ||
346 ss2->sum_x_sq != ss->sum_x_sq) {
347 td_verror(td, EINVAL, "job rejected: steadystate options must be consistent within reporting groups");
348 return 1;
349 }
350 }
da8f124f 351 } end_for_each();
f0c50c66 352
56a90eba 353 return 0;
f0c50c66 354}
d685adfb 355
9d193019 356uint64_t steadystate_bw_mean(struct thread_stat *ts)
d685adfb
VF
357{
358 int i;
9d193019 359 uint64_t sum;
90e678ba
CL
360 int intervals = ts->ss_dur / (ss_check_interval / 1000L);
361
9fac0db7
JA
362 if (!ts->ss_dur)
363 return 0;
364
90e678ba 365 for (i = 0, sum = 0; i < intervals; i++)
bb49c8bd 366 sum += ts->ss_bw_data[i];
d685adfb 367
90e678ba 368 return sum / intervals;
d685adfb
VF
369}
370
9d193019 371uint64_t steadystate_iops_mean(struct thread_stat *ts)
d685adfb
VF
372{
373 int i;
9d193019 374 uint64_t sum;
90e678ba 375 int intervals = ts->ss_dur / (ss_check_interval / 1000L);
d685adfb 376
9fac0db7
JA
377 if (!ts->ss_dur)
378 return 0;
379
90e678ba 380 for (i = 0, sum = 0; i < intervals; i++)
bb49c8bd 381 sum += ts->ss_iops_data[i];
d685adfb 382
90e678ba 383 return sum / intervals;
d685adfb 384}