memcpy: add hybrid
[fio.git] / lib / memcpy.c
... / ...
CommitLineData
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
15struct memcpy_test {
16 const char *name;
17 void *src;
18 void *dst;
19 size_t size;
20};
21
22static 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
72struct memcpy_type {
73 const char *name;
74 unsigned int mask;
75 void (*fn)(struct memcpy_test *);
76};
77
78enum {
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
106static void t_memcpy(struct memcpy_test *test)
107{
108 do_test(test, memcpy);
109}
110
111static void t_memmove(struct memcpy_test *test)
112{
113 do_test(test, memmove);
114}
115
116static 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
125static void t_simple(struct memcpy_test *test)
126{
127 do_test(test, simple_memcpy);
128}
129
130static 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
138static 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
164static 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
186static 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
196static 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
223static void free_tests(void)
224{
225 free(tests[0].src);
226 free(tests[0].dst);
227}
228
229int 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}