#include #include #include #include #include "memcpy.h" #include "rand.h" #include "../fio_time.h" #include "../gettime.h" #include "../os/os.h" #define BUF_SIZE 32 * 1024 * 1024ULL #define NR_ITERS 64 struct memcpy_test { const char *name; void *src; void *dst; size_t size; }; static struct memcpy_test tests[] = { { .name = "8 bytes", .size = 8, }, { .name = "16 bytes", .size = 16, }, { .name = "96 bytes", .size = 96, }, { .name = "128 bytes", .size = 128, }, { .name = "256 bytes", .size = 256, }, { .name = "512 bytes", .size = 512, }, { .name = "2048 bytes", .size = 2048, }, { .name = "8192 bytes", .size = 8192, }, { .name = "131072 bytes", .size = 131072, }, { .name = "262144 bytes", .size = 262144, }, { .name = "524288 bytes", .size = 524288, }, { .name = NULL, }, }; struct memcpy_type { const char *name; unsigned int mask; void (*fn)(struct memcpy_test *); }; enum { T_MEMCPY = 1U << 0, T_MEMMOVE = 1U << 1, T_SIMPLE = 1U << 2, T_HYBRID = 1U << 3, }; #define do_test(test, fn) do { \ size_t left, this; \ void *src, *dst; \ int i; \ \ for (i = 0; i < NR_ITERS; i++) { \ left = BUF_SIZE; \ src = test->src; \ dst = test->dst; \ while (left) { \ this = test->size; \ if (this > left) \ this = left; \ (fn)(dst, src, this); \ left -= this; \ src += this; \ dst += this; \ } \ } \ } while (0) static void t_memcpy(struct memcpy_test *test) { do_test(test, memcpy); } static void t_memmove(struct memcpy_test *test) { do_test(test, memmove); } static void simple_memcpy(void *dst, void const *src, size_t len) { char *d = dst; const char *s = src; while (len--) *d++ = *s++; } static void t_simple(struct memcpy_test *test) { do_test(test, simple_memcpy); } static void t_hybrid(struct memcpy_test *test) { if (test->size >= 64) do_test(test, simple_memcpy); else do_test(test, memcpy); } static struct memcpy_type t[] = { { .name = "memcpy", .mask = T_MEMCPY, .fn = t_memcpy, }, { .name = "memmove", .mask = T_MEMMOVE, .fn = t_memmove, }, { .name = "simple", .mask = T_SIMPLE, .fn = t_simple, }, { .name = "hybrid", .mask = T_HYBRID, .fn = t_hybrid, }, { .name = NULL, }, }; static unsigned int get_test_mask(const char *type) { char *ostr, *str = strdup(type); unsigned int mask; char *name; int i; ostr = str; mask = 0; while ((name = strsep(&str, ",")) != NULL) { for (i = 0; t[i].name; i++) { if (!strcmp(t[i].name, name)) { mask |= t[i].mask; break; } } } free(ostr); return mask; } static int list_types(void) { int i; for (i = 0; t[i].name; i++) printf("%s\n", t[i].name); return 1; } static int setup_tests(void) { struct memcpy_test *test; struct frand_state state; void *src, *dst; int i; src = malloc(BUF_SIZE); dst = malloc(BUF_SIZE); if (!src || !dst) { free(src); free(dst); return 1; } init_rand_seed(&state, 0x8989, 0); fill_random_buf(&state, src, BUF_SIZE); for (i = 0; tests[i].name; i++) { test = &tests[i]; test->src = src; test->dst = dst; } return 0; } static void free_tests(void) { free(tests[0].src); free(tests[0].dst); } int fio_memcpy_test(const char *type) { unsigned int test_mask = 0; int j, i; if (!type) test_mask = ~0U; else if (!strcmp(type, "help") || !strcmp(type, "list")) return list_types(); else test_mask = get_test_mask(type); if (!test_mask) { fprintf(stderr, "fio: unknown hash `%s`. Available:\n", type); return list_types(); } if (setup_tests()) { fprintf(stderr, "setting up mem regions failed\n"); return 1; } for (i = 0; t[i].name; i++) { struct timespec ts; double mb_sec; uint64_t usec; if (!(t[i].mask & test_mask)) continue; /* * For first run, make sure CPUs are spun up and that * we've touched the data. */ usec_spin(100000); t[i].fn(&tests[0]); printf("%s\n", t[i].name); for (j = 0; tests[j].name; j++) { fio_gettime(&ts, NULL); t[i].fn(&tests[j]); usec = utime_since_now(&ts); if (usec) { unsigned long long mb = NR_ITERS * BUF_SIZE; mb_sec = (double) mb / (double) usec; mb_sec /= (1.024 * 1.024); printf("\t%s:\t%8.2f MiB/sec\n", tests[j].name, mb_sec); } else printf("\t%s:inf MiB/sec\n", tests[j].name); } } free_tests(); return 0; }