-
Notifications
You must be signed in to change notification settings - Fork 1.3k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
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 <[email protected]>
- Loading branch information
Showing
3 changed files
with
280 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
#ifndef FIO_MEMCPY_H | ||
#define FIO_MEMCPY_H | ||
|
||
int fio_memcpy_test(const char *type); | ||
|
||
#endif |