Add basic memcpy test
[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_type *, struct memcpy_test *);
76 };
77
78 enum {
79         T_MEMCPY        = 1U << 0,
80         T_MEMMOVE       = 1U << 1,
81         T_SIMPLE        = 1U << 2,
82 };
83
84 #define do_test(t, test, fn)    do {                                    \
85         size_t left, this;                                              \
86         void *src, *dst;                                                \
87         int i;                                                          \
88                                                                         \
89         for (i = 0; i < NR_ITERS; i++) {                                \
90                 left = BUF_SIZE;                                        \
91                 src = test->src;                                        \
92                 dst = test->dst;                                        \
93                 while (left) {                                          \
94                         this = test->size;                              \
95                         if (this > left)                                \
96                                 this = left;                            \
97                         (fn)(dst, src, this);                           \
98                         left -= this;                                   \
99                         src += this;                                    \
100                         dst += this;                                    \
101                 }                                                       \
102         }                                                               \
103 } while (0)
104
105 static void t_memcpy(struct memcpy_type *t, struct memcpy_test *test)
106 {
107         do_test(t, test, memcpy);
108 }
109
110 static void t_memmove(struct memcpy_type *t, struct memcpy_test *test)
111 {
112         do_test(t, test, memmove);
113 }
114
115 static void simple_memcpy(void *dst, void const *src, size_t len)
116 {
117         char *d = dst;
118         const char *s = src;
119
120         while (len--)
121                 *d++ = *s++;
122 }
123
124 static void t_simple(struct memcpy_type *t, struct memcpy_test *test)
125 {
126         do_test(t, test, simple_memcpy);
127 }
128
129 static struct memcpy_type t[] = {
130         {
131                 .name = "memcpy",
132                 .mask = T_MEMCPY,
133                 .fn = t_memcpy,
134         },
135         {
136                 .name = "memmove",
137                 .mask = T_MEMMOVE,
138                 .fn = t_memmove,
139         },
140         {
141                 .name = "simple",
142                 .mask = T_SIMPLE,
143                 .fn = t_simple,
144         },
145
146         {
147                 .name = NULL,
148         },
149 };
150
151 static unsigned int get_test_mask(const char *type)
152 {
153         char *ostr, *str = strdup(type);
154         unsigned int mask;
155         char *name;
156         int i;
157
158         ostr = str;
159         mask = 0;
160         while ((name = strsep(&str, ",")) != NULL) {
161                 for (i = 0; t[i].name; i++) {
162                         if (!strcmp(t[i].name, name)) {
163                                 mask |= t[i].mask;
164                                 break;
165                         }
166                 }
167         }
168
169         free(ostr);
170         return mask;
171 }
172
173 static int list_types(void)
174 {
175         int i;
176
177         for (i = 0; t[i].name; i++)
178                 printf("%s\n", t[i].name);
179
180         return 1;
181 }
182
183 static int setup_tests(void)
184 {
185         struct memcpy_test *test;
186         struct frand_state state;
187         void *src, *dst;
188         int i;
189
190         if (posix_memalign(&src, page_size, BUF_SIZE))
191                 return 1;
192         if (posix_memalign(&dst, page_size, BUF_SIZE))
193                 return 1;
194
195         init_rand_seed(&state, 0x8989, 0);
196         fill_random_buf(&state, src, BUF_SIZE);
197
198         for (i = 0; tests[i].name; i++) {
199                 test = &tests[i];
200                 test->src = src;
201                 test->dst = dst;
202         }
203
204         return 0;
205 }
206
207 int fio_memcpy_test(const char *type)
208 {
209         unsigned int test_mask = 0;
210         int j, i;
211
212         if (!type)
213                 test_mask = ~0U;
214         else if (!strcmp(type, "help") || !strcmp(type, "list"))
215                 return list_types();
216         else
217                 test_mask = get_test_mask(type);
218
219         if (!test_mask) {
220                 fprintf(stderr, "fio: unknown hash `%s`. Available:\n", type);
221                 return list_types();
222         }
223
224         if (setup_tests()) {
225                 fprintf(stderr, "setting up mem regions failed\n");
226                 return 1;
227         }
228
229         for (i = 0; t[i].name; i++) {
230                 struct timespec ts;
231                 double mb_sec;
232                 uint64_t usec;
233
234                 if (!(t[i].mask & test_mask))
235                         continue;
236
237                 /*
238                  * For first run, make sure CPUs are spun up and that
239                  * we've touched the data.
240                  */
241                 usec_spin(100000);
242                 t[i].fn(&t[i], &tests[0]);
243
244                 printf("%s\n", t[i].name);
245
246                 for (j = 0; tests[j].name; j++) {
247                         fio_gettime(&ts, NULL);
248                         t[i].fn(&t[i], &tests[j]);
249                         usec = utime_since_now(&ts);
250
251                         if (usec) {
252                                 unsigned long long mb = NR_ITERS * BUF_SIZE;
253
254                                 mb_sec = (double) mb / (double) usec;
255                                 mb_sec /= (1.024 * 1.024);
256                                 printf("\t%s:\t%8.2f MiB/sec\n", tests[j].name, mb_sec);
257                         } else
258                                 printf("\t%s:inf MiB/sec\n", tests[j].name);
259                 }
260         }
261
262         return 0;
263 }