Commit | Line | Data |
---|---|---|
da751ca9 JA |
1 | /* |
2 | * CPU engine | |
3 | * | |
4 | * Doesn't transfer any data, merely burns CPU cycles according to | |
5 | * the settings. | |
6 | * | |
7 | */ | |
5f350952 | 8 | #include "../fio.h" |
d220c761 | 9 | #include "../optgroup.h" |
2866c82d | 10 | |
9de473a8 EV |
11 | // number of 32 bit integers to sort |
12 | size_t qsort_size = (256 * (1ULL << 10)); // 256KB | |
13 | ||
14 | struct mwc_t { | |
15 | uint32_t w; | |
16 | uint32_t z; | |
17 | }; | |
18 | ||
19 | enum stress_mode { | |
20 | FIO_CPU_NOOP = 0, | |
21 | FIO_CPU_QSORT = 1, | |
22 | }; | |
23 | ||
0353050f | 24 | struct cpu_options { |
a1f871c7 | 25 | void *pad; |
0353050f JA |
26 | unsigned int cpuload; |
27 | unsigned int cpucycle; | |
9de473a8 | 28 | enum stress_mode cpumode; |
046395d7 | 29 | unsigned int exit_io_done; |
9de473a8 | 30 | int32_t *qsort_data; |
0353050f JA |
31 | }; |
32 | ||
33 | static struct fio_option options[] = { | |
34 | { | |
35 | .name = "cpuload", | |
36 | .lname = "CPU load", | |
37 | .type = FIO_OPT_INT, | |
38 | .off1 = offsetof(struct cpu_options, cpuload), | |
39 | .help = "Use this percentage of CPU", | |
cbd9148f | 40 | .category = FIO_OPT_C_ENGINE, |
0353050f JA |
41 | .group = FIO_OPT_G_INVALID, |
42 | }, | |
9de473a8 EV |
43 | { |
44 | .name = "cpumode", | |
45 | .lname = "cpumode", | |
46 | .type = FIO_OPT_STR, | |
47 | .help = "Stress mode", | |
48 | .off1 = offsetof(struct cpu_options, cpumode), | |
49 | .def = "noop", | |
50 | .posval = { | |
51 | { .ival = "noop", | |
52 | .oval = FIO_CPU_NOOP, | |
53 | .help = "NOOP instructions", | |
54 | }, | |
55 | { .ival = "qsort", | |
56 | .oval = FIO_CPU_QSORT, | |
57 | .help = "QSORT computation", | |
58 | }, | |
59 | }, | |
60 | .category = FIO_OPT_C_ENGINE, | |
61 | .group = FIO_OPT_G_INVALID, | |
62 | }, | |
0353050f JA |
63 | { |
64 | .name = "cpuchunks", | |
65 | .lname = "CPU chunk", | |
66 | .type = FIO_OPT_INT, | |
67 | .off1 = offsetof(struct cpu_options, cpucycle), | |
68 | .help = "Length of the CPU burn cycles (usecs)", | |
69 | .def = "50000", | |
70 | .parent = "cpuload", | |
71 | .hide = 1, | |
cbd9148f | 72 | .category = FIO_OPT_C_ENGINE, |
0353050f JA |
73 | .group = FIO_OPT_G_INVALID, |
74 | }, | |
046395d7 JA |
75 | { |
76 | .name = "exit_on_io_done", | |
77 | .lname = "Exit when IO threads are done", | |
78 | .type = FIO_OPT_BOOL, | |
79 | .off1 = offsetof(struct cpu_options, exit_io_done), | |
80 | .help = "Exit when IO threads finish", | |
81 | .def = "0", | |
cbd9148f | 82 | .category = FIO_OPT_C_ENGINE, |
046395d7 JA |
83 | .group = FIO_OPT_G_INVALID, |
84 | }, | |
0353050f JA |
85 | { |
86 | .name = NULL, | |
87 | }, | |
88 | }; | |
89 | ||
9de473a8 EV |
90 | /* |
91 | * mwc32() | |
92 | * Multiply-with-carry random numbers | |
93 | * fast pseudo random number generator, see | |
94 | * http://www.cse.yorku.ca/~oz/marsaglia-rng.html | |
95 | */ | |
96 | uint32_t mwc32(struct mwc_t *mwc) | |
97 | { | |
98 | mwc->z = 36969 * (mwc->z & 65535) + (mwc->z >> 16); | |
99 | mwc->w = 18000 * (mwc->w & 65535) + (mwc->w >> 16); | |
100 | return (mwc->z << 16) + mwc->w; | |
101 | } | |
102 | ||
103 | /* | |
104 | * stress_qsort_cmp_1() | |
105 | * qsort comparison - sort on int32 values | |
106 | */ | |
107 | static int stress_qsort_cmp_1(const void *p1, const void *p2) | |
108 | { | |
109 | const int32_t *i1 = (const int32_t *)p1; | |
110 | const int32_t *i2 = (const int32_t *)p2; | |
111 | ||
112 | if (*i1 > *i2) | |
113 | return 1; | |
114 | else if (*i1 < *i2) | |
115 | return -1; | |
116 | else | |
117 | return 0; | |
118 | } | |
119 | ||
120 | /* | |
121 | * stress_qsort_cmp_2() | |
122 | * qsort comparison - reverse sort on int32 values | |
123 | */ | |
124 | static int stress_qsort_cmp_2(const void *p1, const void *p2) | |
125 | { | |
126 | return stress_qsort_cmp_1(p2, p1); | |
127 | } | |
128 | ||
129 | /* | |
130 | * stress_qsort_cmp_3() | |
131 | * qsort comparison - sort on int8 values | |
132 | */ | |
133 | static int stress_qsort_cmp_3(const void *p1, const void *p2) | |
134 | { | |
135 | const int8_t *i1 = (const int8_t *)p1; | |
136 | const int8_t *i2 = (const int8_t *)p2; | |
137 | ||
138 | /* Force re-ordering on 8 bit value */ | |
139 | return *i1 - *i2; | |
140 | } | |
141 | ||
142 | static int do_qsort(struct thread_data *td) | |
143 | { | |
144 | struct thread_options *o = &td->o; | |
145 | struct cpu_options *co = td->eo; | |
146 | struct timespec start, now; | |
147 | fio_get_mono_time(&start); | |
148 | /* Sort "random" data */ | |
149 | qsort(co->qsort_data, qsort_size, sizeof(*(co->qsort_data)), stress_qsort_cmp_1); | |
150 | ||
151 | /* Reverse sort */ | |
152 | qsort(co->qsort_data, qsort_size, sizeof(*(co->qsort_data)), stress_qsort_cmp_2); | |
153 | ||
154 | /* And re-order by byte compare */ | |
155 | qsort((uint8_t *)co->qsort_data, qsort_size * 4, sizeof(uint8_t), stress_qsort_cmp_3); | |
156 | ||
157 | /* Reverse sort this again */ | |
158 | qsort(co->qsort_data, qsort_size, sizeof(*(co->qsort_data)), stress_qsort_cmp_2); | |
159 | fio_get_mono_time(&now); | |
160 | ||
161 | /* Adjusting cpucycle automatically to be as close as possible to the expected cpuload | |
162 | * The time to execute do_qsort() may change over time as per : | |
163 | * - the job concurrency | |
164 | * - the cpu clock adjusted by the power management | |
165 | * After every do_qsort() call, the next thinktime is adjusted regarding the last run performance | |
166 | */ | |
167 | co->cpucycle = utime_since(&start, &now); | |
168 | o->thinktime = ((unsigned long long) co->cpucycle * (100 - co->cpuload)) / co->cpuload; | |
169 | ||
170 | return 0; | |
171 | } | |
0353050f | 172 | |
2e4ef4fb JA |
173 | static enum fio_q_status fio_cpuio_queue(struct thread_data *td, |
174 | struct io_u fio_unused *io_u) | |
ba0fbe10 | 175 | { |
0353050f JA |
176 | struct cpu_options *co = td->eo; |
177 | ||
046395d7 JA |
178 | if (co->exit_io_done && !fio_running_or_pending_io_threads()) { |
179 | td->done = 1; | |
180 | return FIO_Q_BUSY; | |
181 | } | |
182 | ||
9de473a8 EV |
183 | switch (co->cpumode) { |
184 | case FIO_CPU_NOOP: | |
185 | usec_spin(co->cpucycle); | |
186 | break; | |
187 | case FIO_CPU_QSORT: | |
188 | do_qsort(td); | |
189 | break; | |
190 | } | |
191 | ||
ba0fbe10 JA |
192 | return FIO_Q_COMPLETED; |
193 | } | |
194 | ||
9de473a8 EV |
195 | static int noop_init(struct thread_data *td) |
196 | { | |
197 | struct cpu_options *co = td->eo; | |
198 | ||
199 | log_info("%s (noop): ioengine=%s, cpuload=%u, cpucycle=%u\n", | |
200 | td->o.name, td->io_ops->name, co->cpuload, co->cpucycle); | |
201 | return 0; | |
202 | } | |
203 | ||
204 | static int qsort_cleanup(struct thread_data *td) | |
205 | { | |
206 | struct cpu_options *co = td->eo; | |
207 | if (co->qsort_data) | |
208 | free(co->qsort_data); | |
209 | return 0; | |
210 | } | |
211 | ||
212 | static int qsort_init(struct thread_data *td) | |
213 | { | |
214 | struct cpu_options *co = td->eo; | |
215 | ||
216 | // Setting up a default entropy | |
217 | struct mwc_t mwc = { | |
218 | (521288629UL), | |
219 | (362436069UL) | |
220 | }; | |
221 | ||
222 | co->qsort_data = calloc(qsort_size, sizeof(*co->qsort_data)); | |
223 | if (co->qsort_data == NULL) { | |
224 | td_verror(td, ENOMEM, "qsort_init"); | |
225 | return 1; | |
226 | } | |
227 | ||
228 | /* This is expensive, init the memory once */ | |
229 | for (int32_t *ptr = co->qsort_data, i = 0; i < qsort_size; i++) | |
230 | *ptr++ = mwc32(&mwc); | |
231 | ||
232 | log_info("%s (qsort): ioengine=%s, cpuload=%u, cpucycle=%u\n", | |
233 | td->o.name, td->io_ops->name, co->cpuload, co->cpucycle); | |
234 | ||
235 | return 0; | |
236 | } | |
237 | ||
2866c82d JA |
238 | static int fio_cpuio_init(struct thread_data *td) |
239 | { | |
2dc1bbeb | 240 | struct thread_options *o = &td->o; |
0353050f | 241 | struct cpu_options *co = td->eo; |
9de473a8 | 242 | int td_previous_state; |
2dc1bbeb | 243 | |
0353050f | 244 | if (!co->cpuload) { |
ba0fbe10 | 245 | td_vmsg(td, EINVAL, "cpu thread needs rate (cpuload=)","cpuio"); |
2866c82d | 246 | return 1; |
ba0fbe10 JA |
247 | } |
248 | ||
0353050f JA |
249 | if (co->cpuload > 100) |
250 | co->cpuload = 100; | |
2866c82d | 251 | |
9de473a8 EV |
252 | // Saving the current thread state |
253 | td_previous_state = td->runstate; | |
254 | /* Reporting that we are preparing the engine | |
255 | * This is useful as the qsort() calibration takes time | |
256 | * This prevents the job from starting before init is completed | |
257 | */ | |
258 | td_set_runstate(td, TD_SETTING_UP); | |
259 | ||
ba0fbe10 JA |
260 | /* |
261 | * set thinktime_sleep and thinktime_spin appropriately | |
262 | */ | |
2dc1bbeb JA |
263 | o->thinktime_blocks = 1; |
264 | o->thinktime_spin = 0; | |
2bce52c6 | 265 | o->thinktime = ((unsigned long long) co->cpucycle * (100 - co->cpuload)) / co->cpuload; |
2866c82d | 266 | |
2dc1bbeb | 267 | o->nr_files = o->open_files = 1; |
0353050f | 268 | |
9de473a8 EV |
269 | switch (co->cpumode) { |
270 | case FIO_CPU_NOOP: | |
271 | noop_init(td); | |
272 | break; | |
273 | case FIO_CPU_QSORT: | |
274 | qsort_init(td); | |
275 | break; | |
276 | } | |
0353050f | 277 | |
9de473a8 EV |
278 | // Let's restore the previous state. |
279 | td_set_runstate(td, td_previous_state); | |
ba0fbe10 JA |
280 | return 0; |
281 | } | |
282 | ||
9de473a8 EV |
283 | static void fio_cpuio_cleanup(struct thread_data *td) |
284 | { | |
285 | struct cpu_options *co = td->eo; | |
286 | ||
287 | switch (co->cpumode) { | |
288 | case FIO_CPU_NOOP: | |
289 | break; | |
290 | case FIO_CPU_QSORT: | |
291 | qsort_cleanup(td); | |
292 | break; | |
293 | } | |
294 | } | |
295 | ||
f4e62a5f JA |
296 | static int fio_cpuio_open(struct thread_data fio_unused *td, |
297 | struct fio_file fio_unused *f) | |
ba0fbe10 | 298 | { |
2866c82d JA |
299 | return 0; |
300 | } | |
301 | ||
5f350952 | 302 | static struct ioengine_ops ioengine = { |
2866c82d JA |
303 | .name = "cpuio", |
304 | .version = FIO_IOOPS_VERSION, | |
ba0fbe10 | 305 | .queue = fio_cpuio_queue, |
2866c82d | 306 | .init = fio_cpuio_init, |
9de473a8 | 307 | .cleanup = fio_cpuio_cleanup, |
ba0fbe10 | 308 | .open_file = fio_cpuio_open, |
1f809d15 | 309 | .flags = FIO_SYNCIO | FIO_DISKLESSIO | FIO_NOIO, |
0353050f JA |
310 | .options = options, |
311 | .option_struct_size = sizeof(struct cpu_options), | |
2866c82d | 312 | }; |
5f350952 JA |
313 | |
314 | static void fio_init fio_cpuio_register(void) | |
315 | { | |
316 | register_ioengine(&ioengine); | |
317 | } | |
318 | ||
319 | static void fio_exit fio_cpuio_unregister(void) | |
320 | { | |
321 | unregister_ioengine(&ioengine); | |
322 | } |