fio: fix aio trim completion latencies
[fio.git] / lib / memcpy.c
1 #include <inttypes.h>
2 #include <stdio.h>
3 #include <stdlib.h>
4 #include <string.h>
5
6 #include "memcpy.h"
7 #include "rand.h"
8 #include "../fio_time.h"
9 #include "../gettime.h"
10 #include "../os/os.h"
11
12 #define BUF_SIZE        32 * 1024 * 1024ULL
13
14 #define NR_ITERS        64
15
16 struct memcpy_test {
17         const char *name;
18         void *src;
19         void *dst;
20         size_t size;
21 };
22
23 static struct memcpy_test tests[] = {
24         {
25                 .name           = "8 bytes",
26                 .size           = 8,
27         },
28         {
29                 .name           = "16 bytes",
30                 .size           = 16,
31         },
32         {
33                 .name           = "96 bytes",
34                 .size           = 96,
35         },
36         {
37                 .name           = "128 bytes",
38                 .size           = 128,
39         },
40         {
41                 .name           = "256 bytes",
42                 .size           = 256,
43         },
44         {
45                 .name           = "512 bytes",
46                 .size           = 512,
47         },
48         {
49                 .name           = "2048 bytes",
50                 .size           = 2048,
51         },
52         {
53                 .name           = "8192 bytes",
54                 .size           = 8192,
55         },
56         {
57                 .name           = "131072 bytes",
58                 .size           = 131072,
59         },
60         {
61                 .name           = "262144 bytes",
62                 .size           = 262144,
63         },
64         {
65                 .name           = "524288 bytes",
66                 .size           = 524288,
67         },
68         {
69                 .name           = NULL,
70         },
71 };
72
73 struct memcpy_type {
74         const char *name;
75         unsigned int mask;
76         void (*fn)(struct memcpy_test *);
77 };
78
79 enum {
80         T_MEMCPY        = 1U << 0,
81         T_MEMMOVE       = 1U << 1,
82         T_SIMPLE        = 1U << 2,
83         T_HYBRID        = 1U << 3,
84 };
85
86 #define do_test(test, fn)       do {                                    \
87         size_t left, this;                                              \
88         void *src, *dst;                                                \
89         int i;                                                          \
90                                                                         \
91         for (i = 0; i < NR_ITERS; i++) {                                \
92                 left = BUF_SIZE;                                        \
93                 src = test->src;                                        \
94                 dst = test->dst;                                        \
95                 while (left) {                                          \
96                         this = test->size;                              \
97                         if (this > left)                                \
98                                 this = left;                            \
99                         (fn)(dst, src, this);                           \
100                         left -= this;                                   \
101                         src += this;                                    \
102                         dst += this;                                    \
103                 }                                                       \
104         }                                                               \
105 } while (0)
106
107 static void t_memcpy(struct memcpy_test *test)
108 {
109         do_test(test, memcpy);
110 }
111
112 static void t_memmove(struct memcpy_test *test)
113 {
114         do_test(test, memmove);
115 }
116
117 static void simple_memcpy(void *dst, void const *src, size_t len)
118 {
119         char *d = dst;
120         const char *s = src;
121
122         while (len--)
123                 *d++ = *s++;
124 }
125
126 static void t_simple(struct memcpy_test *test)
127 {
128         do_test(test, simple_memcpy);
129 }
130
131 static void t_hybrid(struct memcpy_test *test)
132 {
133         if (test->size >= 64)
134                 do_test(test, simple_memcpy);
135         else
136                 do_test(test, memcpy);
137 }
138
139 static struct memcpy_type t[] = {
140         {
141                 .name = "memcpy",
142                 .mask = T_MEMCPY,
143                 .fn = t_memcpy,
144         },
145         {
146                 .name = "memmove",
147                 .mask = T_MEMMOVE,
148                 .fn = t_memmove,
149         },
150         {
151                 .name = "simple",
152                 .mask = T_SIMPLE,
153                 .fn = t_simple,
154         },
155         {
156                 .name = "hybrid",
157                 .mask = T_HYBRID,
158                 .fn = t_hybrid,
159         },
160         {
161                 .name = NULL,
162         },
163 };
164
165 static unsigned int get_test_mask(const char *type)
166 {
167         char *ostr, *str = strdup(type);
168         unsigned int mask;
169         char *name;
170         int i;
171
172         ostr = str;
173         mask = 0;
174         while ((name = strsep(&str, ",")) != NULL) {
175                 for (i = 0; t[i].name; i++) {
176                         if (!strcmp(t[i].name, name)) {
177                                 mask |= t[i].mask;
178                                 break;
179                         }
180                 }
181         }
182
183         free(ostr);
184         return mask;
185 }
186
187 static int list_types(void)
188 {
189         int i;
190
191         for (i = 0; t[i].name; i++)
192                 printf("%s\n", t[i].name);
193
194         return 1;
195 }
196
197 static int setup_tests(void)
198 {
199         struct memcpy_test *test;
200         struct frand_state state;
201         void *src, *dst;
202         int i;
203
204         src = malloc(BUF_SIZE);
205         dst = malloc(BUF_SIZE);
206         if (!src || !dst) {
207                 free(src);
208                 free(dst);
209                 return 1;
210         }
211
212         init_rand_seed(&state, 0x8989, 0);
213         fill_random_buf(&state, src, BUF_SIZE);
214
215         for (i = 0; tests[i].name; i++) {
216                 test = &tests[i];
217                 test->src = src;
218                 test->dst = dst;
219         }
220
221         return 0;
222 }
223
224 static void free_tests(void)
225 {
226         free(tests[0].src);
227         free(tests[0].dst);
228 }
229
230 int fio_memcpy_test(const char *type)
231 {
232         unsigned int test_mask = 0;
233         int j, i;
234
235         if (!type)
236                 test_mask = ~0U;
237         else if (!strcmp(type, "help") || !strcmp(type, "list"))
238                 return list_types();
239         else
240                 test_mask = get_test_mask(type);
241
242         if (!test_mask) {
243                 fprintf(stderr, "fio: unknown hash `%s`. Available:\n", type);
244                 return list_types();
245         }
246
247         if (setup_tests()) {
248                 fprintf(stderr, "setting up mem regions failed\n");
249                 return 1;
250         }
251
252         for (i = 0; t[i].name; i++) {
253                 struct timespec ts;
254                 double mb_sec;
255                 uint64_t usec;
256
257                 if (!(t[i].mask & test_mask))
258                         continue;
259
260                 /*
261                  * For first run, make sure CPUs are spun up and that
262                  * we've touched the data.
263                  */
264                 usec_spin(100000);
265                 t[i].fn(&tests[0]);
266
267                 printf("%s\n", t[i].name);
268
269                 for (j = 0; tests[j].name; j++) {
270                         fio_gettime(&ts, NULL);
271                         t[i].fn(&tests[j]);
272                         usec = utime_since_now(&ts);
273
274                         if (usec) {
275                                 unsigned long long mb = NR_ITERS * BUF_SIZE;
276
277                                 mb_sec = (double) mb / (double) usec;
278                                 mb_sec /= (1.024 * 1.024);
279                                 printf("\t%s:\t%8.2f MiB/sec\n", tests[j].name, mb_sec);
280                         } else
281                                 printf("\t%s:inf MiB/sec\n", tests[j].name);
282                 }
283         }
284
285         free_tests();
286         return 0;
287 }