Add basic memcpy test
authorJens Axboe <axboe@kernel.dk>
Fri, 1 Dec 2017 18:13:23 +0000 (11:13 -0700)
committerJens Axboe <axboe@kernel.dk>
Fri, 1 Dec 2017 18:13:23 +0000 (11:13 -0700)
Research is ongoing in how to improve (and make deterministic) the
memcpy speed, since it's especially applicable to the persistent
memory engines.

Not documented yet.

Signed-off-by: Jens Axboe <axboe@kernel.dk>
init.c
lib/memcpy.c [new file with mode: 0644]
lib/memcpy.h [new file with mode: 0644]

diff --git a/init.c b/init.c
index 607f7e0c29a472443b8bf90466ffe5ea5f8be4cf..c34bd1599661597b31b4701e9c8dfa00cb06e8fa 100644 (file)
--- a/init.c
+++ b/init.c
@@ -32,6 +32,7 @@
 
 #include "crc/test.h"
 #include "lib/pow2.h"
 
 #include "crc/test.h"
 #include "lib/pow2.h"
+#include "lib/memcpy.h"
 
 const char fio_version_string[] = FIO_VERSION;
 
 
 const char fio_version_string[] = FIO_VERSION;
 
@@ -233,6 +234,11 @@ static struct option l_opts[FIO_NR_OPTIONS] = {
                .has_arg        = optional_argument,
                .val            = 'G',
        },
                .has_arg        = optional_argument,
                .val            = 'G',
        },
+       {
+               .name           = (char *) "memcpytest",
+               .has_arg        = optional_argument,
+               .val            = 'M',
+       },
        {
                .name           = (char *) "idle-prof",
                .has_arg        = required_argument,
        {
                .name           = (char *) "idle-prof",
                .has_arg        = required_argument,
@@ -2731,6 +2737,11 @@ int parse_cmd_line(int argc, char *argv[], int client_type)
                        do_exit++;
                        exit_val = fio_crctest(optarg);
                        break;
                        do_exit++;
                        exit_val = fio_crctest(optarg);
                        break;
+               case 'M':
+                       did_arg = true;
+                       do_exit++;
+                       exit_val = fio_memcpy_test(optarg);
+                       break;
                case 'L': {
                        long long val;
 
                case 'L': {
                        long long val;
 
diff --git a/lib/memcpy.c b/lib/memcpy.c
new file mode 100644 (file)
index 0000000..f937dd9
--- /dev/null
@@ -0,0 +1,263 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "memcpy.h"
+#include "rand.h"
+#include "../fio_time.h"
+#include "../gettime.h"
+#include "../fio.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_type *, struct memcpy_test *);
+};
+
+enum {
+       T_MEMCPY        = 1U << 0,
+       T_MEMMOVE       = 1U << 1,
+       T_SIMPLE        = 1U << 2,
+};
+
+#define do_test(t, 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_type *t, struct memcpy_test *test)
+{
+       do_test(t, test, memcpy);
+}
+
+static void t_memmove(struct memcpy_type *t, struct memcpy_test *test)
+{
+       do_test(t, 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_type *t, struct memcpy_test *test)
+{
+       do_test(t, test, simple_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 = 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;
+
+       if (posix_memalign(&src, page_size, BUF_SIZE))
+               return 1;
+       if (posix_memalign(&dst, page_size, BUF_SIZE))
+               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;
+}
+
+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(&t[i], &tests[0]);
+
+               printf("%s\n", t[i].name);
+
+               for (j = 0; tests[j].name; j++) {
+                       fio_gettime(&ts, NULL);
+                       t[i].fn(&t[i], &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);
+               }
+       }
+
+       return 0;
+}
diff --git a/lib/memcpy.h b/lib/memcpy.h
new file mode 100644 (file)
index 0000000..f61a4a0
--- /dev/null
@@ -0,0 +1,6 @@
+#ifndef FIO_MEMCPY_H
+#define FIO_MEMCPY_H
+
+int fio_memcpy_test(const char *type);
+
+#endif