act: bring closer to real ACT
[fio.git] / profiles / act.c
1 #include "../fio.h"
2 #include "../profile.h"
3 #include "../parse.h"
4
5 /*
6  * 1x loads
7  */
8 #define R_LOAD          2000
9 #define W_LOAD          1000
10
11 #define SAMPLE_SEC      3600            /* 1h checks */
12
13 struct act_pass_criteria {
14         unsigned int max_usec;
15         unsigned int max_perm;
16 };
17 #define ACT_MAX_CRIT    3
18
19 static struct act_pass_criteria act_pass[ACT_MAX_CRIT] = {
20         {
21                 .max_usec =     1000,
22                 .max_perm =     50,
23         },
24         {
25                 .max_usec =     8000,
26                 .max_perm =     10,
27         },
28         {
29                 .max_usec =     64000,
30                 .max_perm =     1,
31         },
32 };
33
34 struct act_prof_data {
35         struct timeval sample_tv;
36         uint64_t lat_buckets[ACT_MAX_CRIT];
37         uint64_t total_ios;
38 };
39
40 static char *device_names;
41 static unsigned int load = 1;
42 static unsigned int prep;
43 static unsigned int threads_per_queue;
44 static unsigned int num_read_blocks;
45 static unsigned int write_size;
46
47 #define ACT_MAX_OPTS    128
48 static const char *act_opts[ACT_MAX_OPTS] = {
49         "direct=1",
50         "ioengine=sync",
51         "random_generator=lfsr",
52         "group_reporting=1",
53         NULL,
54 };
55 static unsigned int opt_idx = 4;
56 static unsigned int org_idx;
57
58 static int act_add_opt(const char *format, ...) __attribute__ ((__format__ (__printf__, 1, 2)));
59
60 static struct fio_option options[] = {
61         {
62                 .name   = "device-names",
63                 .lname  = "device-names",
64                 .type   = FIO_OPT_STR_STORE,
65                 .roff1  = &device_names,
66                 .help   = "Devices to use",
67                 .category = FIO_OPT_C_PROFILE,
68                 .group  = FIO_OPT_G_ACT,
69         },
70         {
71                 .name   = "load",
72                 .lname  = "Load multiplier",
73                 .type   = FIO_OPT_INT,
74                 .roff1  = &load,
75                 .help   = "ACT load multipler (default 1x)",
76                 .def    = "1",
77                 .category = FIO_OPT_C_PROFILE,
78                 .group  = FIO_OPT_G_ACT,
79         },
80         {
81                 .name   = "threads-per-queue",
82                 .lname  = "Number of read IO threads per device",
83                 .type   = FIO_OPT_INT,
84                 .roff1  = &threads_per_queue,
85                 .help   = "Number of read IO threads per device",
86                 .def    = "8",
87                 .category = FIO_OPT_C_PROFILE,
88                 .group  = FIO_OPT_G_ACT,
89         },
90         {
91                 .name   = "read-req-num-512-blocks",
92                 .lname  = "Number of 512b blocks to read",
93                 .type   = FIO_OPT_INT,
94                 .roff1  = &num_read_blocks,
95                 .help   = "Number of 512b blocks to read at the time",
96                 .def    = "3",
97                 .category = FIO_OPT_C_PROFILE,
98                 .group  = FIO_OPT_G_ACT,
99         },
100         {
101                 .name   = "large-block-op-kbytes",
102                 .lname  = "Size of large block ops (writes)",
103                 .type   = FIO_OPT_INT,
104                 .roff1  = &write_size,
105                 .help   = "Size of large block ops (writes)",
106                 .def    = "128k",
107                 .category = FIO_OPT_C_PROFILE,
108                 .group  = FIO_OPT_G_ACT,
109         },
110         {
111                 .name   = "prep",
112                 .lname  = "Run ACT prep phase",
113                 .type   = FIO_OPT_STR_SET,
114                 .roff1  = &prep,
115                 .help   = "Set to run ACT prep phase",
116                 .category = FIO_OPT_C_PROFILE,
117                 .group  = FIO_OPT_G_ACT,
118         },
119         {
120                 .name   = NULL,
121         },
122 };
123
124 static int act_add_opt(const char *str, ...)
125 {
126         char buffer[512];
127         va_list args;
128         size_t len;
129
130         if (opt_idx == ACT_MAX_OPTS) {
131                 log_err("act: ACT_MAX_OPTS is too small\n");
132                 return 1;
133         }
134
135         va_start(args, str);
136         len = vsnprintf(buffer, sizeof(buffer), str, args);
137         va_end(args);
138
139         if (len)
140                 act_opts[opt_idx++] = strdup(buffer);
141
142         return 0;
143 }
144
145 static int act_add_rw(const char *dev, int reads)
146 {
147         if (act_add_opt("name=act-%s-%s", reads ? "read" : "write", dev))
148                 return 1;
149         if (act_add_opt("filename=%s", dev))
150                 return 1;
151         if (act_add_opt("rw=%s", reads ? "randread" : "randwrite"))
152                 return 1;
153         if (reads) {
154                 int rload = load * R_LOAD / threads_per_queue;
155
156                 if (act_add_opt("numjobs=%u", threads_per_queue))
157                         return 1;
158                 if (act_add_opt("rate_iops=%u", rload))
159                         return 1;
160                 if (act_add_opt("bs=%u", num_read_blocks * 512))
161                         return 1;
162         } else {
163                 const int rsize = write_size / (num_read_blocks * 512);
164                 int wload = (load * W_LOAD + rsize - 1) / rsize;
165
166                 if (act_add_opt("rate_iops=%u", wload))
167                         return 1;
168                 if (act_add_opt("bs=%u", write_size))
169                         return 1;
170         }
171
172         return 0;
173 }
174
175 static int act_add_dev_prep(const char *dev)
176 {
177         /* Add sequential zero phase */
178         if (act_add_opt("name=act-prep-zeroes-%s", dev))
179                 return 1;
180         if (act_add_opt("filename=%s", dev))
181                 return 1;
182         if (act_add_opt("bs=1M"))
183                 return 1;
184         if (act_add_opt("zero_buffers"))
185                 return 1;
186         if (act_add_opt("rw=write"))
187                 return 1;
188
189         /* Randomly overwrite device */
190         if (act_add_opt("name=act-prep-salt-%s", dev))
191                 return 1;
192         if (act_add_opt("stonewall"))
193                 return 1;
194         if (act_add_opt("filename=%s", dev))
195                 return 1;
196         if (act_add_opt("bs=4k"))
197                 return 1;
198         if (act_add_opt("ioengine=libaio"))
199                 return 1;
200         if (act_add_opt("iodepth=64"))
201                 return 1;
202         if (act_add_opt("rw=randwrite"))
203                 return 1;
204
205         return 0;
206 }
207
208 static int act_add_dev(const char *dev)
209 {
210         if (prep)
211                 return act_add_dev_prep(dev);
212
213         if (act_add_opt("runtime=24h"))
214                 return 1;
215         if (act_add_opt("time_based=1"))
216                 return 1;
217
218         if (act_add_rw(dev, 1))
219                 return 1;
220         if (act_add_rw(dev, 0))
221                 return 1;
222
223         return 0;
224 }
225
226 /*
227  * Fill our private options into the command line
228  */
229 static int act_prep_cmdline(void)
230 {
231         if (!device_names) {
232                 log_err("act: need device-names\n");
233                 return 1;
234         }
235
236         org_idx = opt_idx;
237
238         do {
239                 char *dev;
240
241                 dev = strsep(&device_names, ",");
242                 if (!dev)
243                         break;
244
245                 if (act_add_dev(dev)) {
246                         log_err("act: failed adding device to the mix\n");
247                         break;
248                 }
249         } while (1);
250
251         return 0;
252 }
253
254 static int act_io_u_lat(struct thread_data *td, uint64_t usec)
255 {
256         struct act_prof_data *apd = td->prof_data;
257         int i, ret = 0;
258         double perm;
259
260         if (prep)
261                 return 0;
262
263         apd->total_ios++;
264
265         for (i = ACT_MAX_CRIT - 1; i >= 0; i--) {
266                 if (usec > act_pass[i].max_usec) {
267                         apd->lat_buckets[i]++;
268                         break;
269                 }
270         }
271
272         if (time_since_now(&apd->sample_tv) < SAMPLE_SEC)
273                 return 0;
274
275         /* SAMPLE_SEC has passed, check criteria for pass */
276         for (i = 0; i < ACT_MAX_CRIT; i++) {
277                 perm = (1000.0 * apd->lat_buckets[i]) / apd->total_ios;
278                 if (perm < act_pass[i].max_perm)
279                         continue;
280
281                 log_err("act: %f%% exceeds pass criteria of %f%%\n", perm / 10.0, (double) act_pass[i].max_perm / 10.0);
282                 ret = 1;
283                 break;
284         }
285
286         memset(apd->lat_buckets, 0, sizeof(apd->lat_buckets));
287         apd->total_ios = 0;
288
289         fio_gettime(&apd->sample_tv, NULL);
290         return ret;
291 }
292
293 static int act_td_init(struct thread_data *td)
294 {
295         struct act_prof_data *apd;
296
297         apd = calloc(sizeof(*apd), 1);
298         fio_gettime(&apd->sample_tv, NULL);
299         td->prof_data = apd;
300         return 0;
301 }
302
303 static void act_td_exit(struct thread_data *td)
304 {
305         free(td->prof_data);
306         td->prof_data = NULL;
307 }
308
309 static struct prof_io_ops act_io_ops = {
310         .td_init        = act_td_init,
311         .td_exit        = act_td_exit,
312         .io_u_lat       = act_io_u_lat,
313 };
314
315 static struct profile_ops act_profile = {
316         .name           = "act",
317         .desc           = "ACT Aerospike like benchmark",
318         .options        = options,
319         .prep_cmd       = act_prep_cmdline,
320         .cmdline        = act_opts,
321         .io_ops         = &act_io_ops,
322 };
323
324 static void fio_init act_register(void)
325 {
326         if (register_profile(&act_profile))
327                 log_err("fio: failed to register profile 'act'\n");
328 }
329
330 static void fio_exit act_unregister(void)
331 {
332         while (org_idx && org_idx < opt_idx)
333                 free((void *) act_opts[++org_idx]);
334
335         unregister_profile(&act_profile);
336 }