Merge branch 'master' of https://github.com/celestinechen/fio
[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         if (!tests[0].name)
205                 return 0;
206
207         src = malloc(BUF_SIZE);
208         dst = malloc(BUF_SIZE);
209         if (!src || !dst) {
210                 free(src);
211                 free(dst);
212                 return 1;
213         }
214
215         init_rand_seed(&state, 0x8989, 0);
216         fill_random_buf(&state, src, BUF_SIZE);
217
218         for (i = 0; tests[i].name; i++) {
219                 test = &tests[i];
220                 test->src = src;
221                 test->dst = dst;
222         }
223
224         return 0;
225 }
226
227 static void free_tests(void)
228 {
229         free(tests[0].src);
230         free(tests[0].dst);
231 }
232
233 int fio_memcpy_test(const char *type)
234 {
235         unsigned int test_mask = 0;
236         int j, i;
237
238         if (!type)
239                 test_mask = ~0U;
240         else if (!strcmp(type, "help") || !strcmp(type, "list"))
241                 return list_types();
242         else
243                 test_mask = get_test_mask(type);
244
245         if (!test_mask) {
246                 fprintf(stderr, "fio: unknown hash `%s`. Available:\n", type);
247                 return list_types();
248         }
249
250         if (setup_tests()) {
251                 fprintf(stderr, "setting up mem regions failed\n");
252                 return 1;
253         }
254
255         for (i = 0; t[i].name; i++) {
256                 struct timespec ts;
257                 double mb_sec;
258                 uint64_t usec;
259
260                 if (!(t[i].mask & test_mask))
261                         continue;
262
263                 /*
264                  * For first run, make sure CPUs are spun up and that
265                  * we've touched the data.
266                  */
267                 usec_spin(100000);
268                 t[i].fn(&tests[0]);
269
270                 printf("%s\n", t[i].name);
271
272                 for (j = 0; tests[j].name; j++) {
273                         fio_gettime(&ts, NULL);
274                         t[i].fn(&tests[j]);
275                         usec = utime_since_now(&ts);
276
277                         if (usec) {
278                                 unsigned long long mb = NR_ITERS * BUF_SIZE;
279
280                                 mb_sec = (double) mb / (double) usec;
281                                 mb_sec /= (1.024 * 1.024);
282                                 printf("\t%s:\t%8.2f MiB/sec\n", tests[j].name, mb_sec);
283                         } else
284                                 printf("\t%s:inf MiB/sec\n", tests[j].name);
285                 }
286         }
287
288         free_tests();
289         return 0;
290 }