t/nvmept_trim: increase transfer size for some tests
[fio.git] / steadystate.c
... / ...
CommitLineData
1#include <stdlib.h>
2
3#include "fio.h"
4#include "steadystate.h"
5
6bool steadystate_enabled = false;
7unsigned int ss_check_interval = 1000;
8
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
17static void steadystate_alloc(struct thread_data *td)
18{
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));
23
24 td->ss.state |= FIO_SS_DATA;
25}
26
27void steadystate_setup(void)
28{
29 struct thread_data *prev_td;
30 int prev_groupid;
31
32 if (!steadystate_enabled)
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;
42 for_each_td(td) {
43 if (!td->ss.dur)
44 continue;
45
46 if (!td->o.group_reporting) {
47 steadystate_alloc(td);
48 continue;
49 }
50
51 if (prev_groupid != td->groupid) {
52 if (prev_td)
53 steadystate_alloc(prev_td);
54 prev_groupid = td->groupid;
55 }
56 prev_td = td;
57 } end_for_each();
58
59 if (prev_td && prev_td->o.group_reporting)
60 steadystate_alloc(prev_td);
61}
62
63static bool steadystate_slope(uint64_t iops, uint64_t bw,
64 struct thread_data *td)
65{
66 int i, j;
67 double result;
68 struct steadystate_data *ss = &td->ss;
69 uint64_t new_val;
70 int intervals = ss->dur / (ss_check_interval / 1000L);
71
72 ss->bw_data[ss->tail] = bw;
73 ss->iops_data[ss->tail] = iops;
74
75 if (ss->state & FIO_SS_IOPS)
76 new_val = iops;
77 else
78 new_val = bw;
79
80 if (ss->state & FIO_SS_BUFFER_FULL || ss->tail - ss->head == intervals - 1) {
81 if (!(ss->state & FIO_SS_BUFFER_FULL)) {
82 /* first time through */
83 for (i = 0, ss->sum_y = 0; i < intervals; i++) {
84 if (ss->state & FIO_SS_IOPS)
85 ss->sum_y += ss->iops_data[i];
86 else
87 ss->sum_y += ss->bw_data[i];
88 j = (ss->head + i) % intervals;
89 if (ss->state & FIO_SS_IOPS)
90 ss->sum_xy += i * ss->iops_data[j];
91 else
92 ss->sum_xy += i * ss->bw_data[j];
93 }
94 ss->state |= FIO_SS_BUFFER_FULL;
95 } else { /* easy to update the sums */
96 ss->sum_y -= ss->oldest_y;
97 ss->sum_y += new_val;
98 ss->sum_xy = ss->sum_xy - ss->sum_y + intervals * new_val;
99 }
100
101 if (ss->state & FIO_SS_IOPS)
102 ss->oldest_y = ss->iops_data[ss->head];
103 else
104 ss->oldest_y = ss->bw_data[ss->head];
105
106 /*
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.
111 */
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);
114 if (ss->state & FIO_SS_PCT)
115 ss->criterion = 100.0 * ss->slope / (ss->sum_y / intervals);
116 else
117 ss->criterion = ss->slope;
118
119 dprint(FD_STEADYSTATE, "sum_y: %llu, sum_xy: %llu, slope: %f, "
120 "criterion: %f, limit: %f\n",
121 (unsigned long long) ss->sum_y,
122 (unsigned long long) ss->sum_xy,
123 ss->slope, ss->criterion, ss->limit);
124
125 result = ss->criterion * (ss->criterion < 0.0 ? -1.0 : 1.0);
126 if (result < ss->limit)
127 return true;
128 }
129
130 ss->tail = (ss->tail + 1) % intervals;
131 if (ss->tail <= ss->head)
132 ss->head = (ss->head + 1) % intervals;
133
134 return false;
135}
136
137static bool steadystate_deviation(uint64_t iops, uint64_t bw,
138 struct thread_data *td)
139{
140 int i;
141 double diff;
142 double mean;
143
144 struct steadystate_data *ss = &td->ss;
145 int intervals = ss->dur / (ss_check_interval / 1000L);
146
147 ss->bw_data[ss->tail] = bw;
148 ss->iops_data[ss->tail] = iops;
149
150 if (ss->state & FIO_SS_BUFFER_FULL || ss->tail - ss->head == intervals - 1) {
151 if (!(ss->state & FIO_SS_BUFFER_FULL)) {
152 /* first time through */
153 for (i = 0, ss->sum_y = 0; i < intervals; i++) {
154 if (ss->state & FIO_SS_IOPS)
155 ss->sum_y += ss->iops_data[i];
156 else
157 ss->sum_y += ss->bw_data[i];
158 }
159 ss->state |= FIO_SS_BUFFER_FULL;
160 } else { /* easy to update the sum */
161 ss->sum_y -= ss->oldest_y;
162 if (ss->state & FIO_SS_IOPS)
163 ss->sum_y += ss->iops_data[ss->tail];
164 else
165 ss->sum_y += ss->bw_data[ss->tail];
166 }
167
168 if (ss->state & FIO_SS_IOPS)
169 ss->oldest_y = ss->iops_data[ss->head];
170 else
171 ss->oldest_y = ss->bw_data[ss->head];
172
173 mean = (double) ss->sum_y / intervals;
174 ss->deviation = 0.0;
175
176 for (i = 0; i < intervals; i++) {
177 if (ss->state & FIO_SS_IOPS)
178 diff = ss->iops_data[i] - mean;
179 else
180 diff = ss->bw_data[i] - mean;
181 ss->deviation = max(ss->deviation, diff * (diff < 0.0 ? -1.0 : 1.0));
182 }
183
184 if (ss->state & FIO_SS_PCT)
185 ss->criterion = 100.0 * ss->deviation / mean;
186 else
187 ss->criterion = ss->deviation;
188
189 dprint(FD_STEADYSTATE, "intervals: %d, sum_y: %llu, mean: %f, max diff: %f, "
190 "objective: %f, limit: %f\n",
191 intervals,
192 (unsigned long long) ss->sum_y, mean,
193 ss->deviation, ss->criterion, ss->limit);
194
195 if (ss->criterion < ss->limit)
196 return true;
197 }
198
199 ss->tail = (ss->tail + 1) % intervals;
200 if (ss->tail == ss->head)
201 ss->head = (ss->head + 1) % intervals;
202
203 return false;
204}
205
206int steadystate_check(void)
207{
208 int ddir, prev_groupid, group_ramp_time_over = 0;
209 unsigned long rate_time;
210 struct timespec now;
211 uint64_t group_bw = 0, group_iops = 0;
212 uint64_t td_iops, td_bytes;
213 bool ret;
214
215 prev_groupid = -1;
216 for_each_td(td) {
217 const bool needs_lock = td_async_processing(td);
218 struct steadystate_data *ss = &td->ss;
219
220 if (!ss->dur || td->runstate <= TD_SETTING_UP ||
221 td->runstate >= TD_EXITED || !ss->state ||
222 ss->state & FIO_SS_ATTAINED)
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);
236 if (ss->ramp_time && !(ss->state & FIO_SS_RAMP_OVER)) {
237 /*
238 * Begin recording data one check interval after ss->ramp_time
239 * has elapsed
240 */
241 if (utime_since(&td->epoch, &now) >= (ss->ramp_time + ss_check_interval * 1000L))
242 ss->state |= FIO_SS_RAMP_OVER;
243 }
244
245 if (needs_lock)
246 __td_io_u_lock(td);
247
248 for (ddir = 0; ddir < DDIR_RWDIR_CNT; ddir++) {
249 td_iops += td->io_blocks[ddir];
250 td_bytes += td->io_bytes[ddir];
251 }
252
253 if (needs_lock)
254 __td_io_u_unlock(td);
255
256 rate_time = mtime_since(&ss->prev_time, &now);
257 memcpy(&ss->prev_time, &now, sizeof(now));
258
259 if (ss->state & FIO_SS_RAMP_OVER) {
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);
264 ++group_ramp_time_over;
265 }
266 ss->prev_iops = td_iops;
267 ss->prev_bytes = td_bytes;
268
269 if (td->o.group_reporting && !(ss->state & FIO_SS_DATA))
270 continue;
271
272 /*
273 * Don't begin checking criterion until ss->ramp_time is over
274 * for at least one thread in group
275 */
276 if (!group_ramp_time_over)
277 continue;
278
279 dprint(FD_STEADYSTATE, "steadystate_check() thread: %d, "
280 "groupid: %u, rate_msec: %ld, "
281 "iops: %llu, bw: %llu, head: %d, tail: %d\n",
282 __td_index, td->groupid, rate_time,
283 (unsigned long long) group_iops,
284 (unsigned long long) group_bw,
285 ss->head, ss->tail);
286
287 if (ss->state & FIO_SS_SLOPE)
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) {
294 for_each_td(td2) {
295 if (td2->groupid == td->groupid) {
296 td2->ss.state |= FIO_SS_ATTAINED;
297 fio_mark_td_terminate(td2);
298 }
299 } end_for_each();
300 } else {
301 ss->state |= FIO_SS_ATTAINED;
302 fio_mark_td_terminate(td);
303 }
304 }
305 } end_for_each();
306 return 0;
307}
308
309int td_steadystate_init(struct thread_data *td)
310{
311 struct steadystate_data *ss = &td->ss;
312 struct thread_options *o = &td->o;
313 int intervals;
314
315 memset(ss, 0, sizeof(*ss));
316
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;
325 ss_check_interval = o->ss_check_interval / 1000L;
326
327 ss->state = o->ss_state;
328 if (!td->ss.ramp_time)
329 ss->state |= FIO_SS_RAMP_OVER;
330
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;
334 }
335
336 /* make sure that ss options are consistent within reporting group */
337 for_each_td(td2) {
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 ||
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 }
351 } end_for_each();
352
353 return 0;
354}
355
356uint64_t steadystate_bw_mean(struct thread_stat *ts)
357{
358 int i;
359 uint64_t sum;
360 int intervals = ts->ss_dur / (ss_check_interval / 1000L);
361
362 if (!ts->ss_dur)
363 return 0;
364
365 for (i = 0, sum = 0; i < intervals; i++)
366 sum += ts->ss_bw_data[i];
367
368 return sum / intervals;
369}
370
371uint64_t steadystate_iops_mean(struct thread_stat *ts)
372{
373 int i;
374 uint64_t sum;
375 int intervals = ts->ss_dur / (ss_check_interval / 1000L);
376
377 if (!ts->ss_dur)
378 return 0;
379
380 for (i = 0, sum = 0; i < intervals; i++)
381 sum += ts->ss_iops_data[i];
382
383 return sum / intervals;
384}