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