blob: 9c18a4b976b634ebeda112728a3369b4b367050c [file] [log] [blame]
Hitoshi Mitake827f3b42009-11-18 00:20:09 +09001/*
2 * mem-memcpy.c
3 *
Ingo Molnar13839ec2015-10-19 10:04:17 +02004 * Simple memcpy() and memset() benchmarks
Hitoshi Mitake827f3b42009-11-18 00:20:09 +09005 *
6 * Written by Hitoshi Mitake <mitake@dcl.info.waseda.ac.jp>
7 */
Hitoshi Mitake827f3b42009-11-18 00:20:09 +09008
9#include "../perf.h"
10#include "../util/util.h"
11#include "../util/parse-options.h"
Hitoshi Mitake827f3b42009-11-18 00:20:09 +090012#include "../util/header.h"
Yann Droneaud57480d22014-06-30 22:28:47 +020013#include "../util/cloexec.h"
Hitoshi Mitake827f3b42009-11-18 00:20:09 +090014#include "bench.h"
Hitoshi Mitake49ce8fc2010-11-25 16:04:52 +090015#include "mem-memcpy-arch.h"
Rabin Vincent5bce1a52014-12-02 16:50:40 +010016#include "mem-memset-arch.h"
Hitoshi Mitake827f3b42009-11-18 00:20:09 +090017
18#include <stdio.h>
19#include <stdlib.h>
20#include <string.h>
21#include <sys/time.h>
22#include <errno.h>
23
24#define K 1024
25
Hitoshi Mitake12eac0b2009-11-20 12:37:17 +090026static const char *length_str = "1MB";
Ingo Molnar27619742015-10-19 10:04:18 +020027static const char *routine = "all";
Jan Beuliche3e877e2012-01-18 13:29:59 +000028static int iterations = 1;
Hitoshi Mitake17d7a112012-07-02 22:46:17 +090029static bool use_cycle;
30static int cycle_fd;
Hitoshi Mitake827f3b42009-11-18 00:20:09 +090031
32static const struct option options[] = {
33 OPT_STRING('l', "length", &length_str, "1MB",
34 "Specify length of memory to copy. "
Namhyung Kim08942f62012-06-20 15:08:06 +090035 "Available units: B, KB, MB, GB and TB (upper and lower)"),
Ingo Molnar27619742015-10-19 10:04:18 +020036 OPT_STRING('r', "routine", &routine, "all",
Borislav Petkovdfecb952015-02-26 19:02:43 +010037 "Specify routine to copy, \"all\" runs all available routines"),
Jan Beuliche3e877e2012-01-18 13:29:59 +000038 OPT_INTEGER('i', "iterations", &iterations,
39 "repeat memcpy() invocation this number of times"),
Hitoshi Mitake17d7a112012-07-02 22:46:17 +090040 OPT_BOOLEAN('c', "cycle", &use_cycle,
Namhyung Kim08942f62012-06-20 15:08:06 +090041 "Use cycles event instead of gettimeofday() for measuring"),
Hitoshi Mitake827f3b42009-11-18 00:20:09 +090042 OPT_END()
43};
44
Hitoshi Mitake49ce8fc2010-11-25 16:04:52 +090045typedef void *(*memcpy_t)(void *, const void *, size_t);
Rabin Vincent5bce1a52014-12-02 16:50:40 +010046typedef void *(*memset_t)(void *, int, size_t);
Hitoshi Mitake49ce8fc2010-11-25 16:04:52 +090047
Hitoshi Mitake827f3b42009-11-18 00:20:09 +090048struct routine {
49 const char *name;
50 const char *desc;
Rabin Vincent308197b2014-12-02 16:50:39 +010051 union {
52 memcpy_t memcpy;
Rabin Vincent5bce1a52014-12-02 16:50:40 +010053 memset_t memset;
Rabin Vincent308197b2014-12-02 16:50:39 +010054 } fn;
Hitoshi Mitake827f3b42009-11-18 00:20:09 +090055};
56
Rabin Vincent308197b2014-12-02 16:50:39 +010057struct routine memcpy_routines[] = {
Ingo Molnar13839ec2015-10-19 10:04:17 +020058 { .name = "default",
59 .desc = "Default memcpy() provided by glibc",
60 .fn.memcpy = memcpy },
61
Ingo Molnar89fe8082013-09-30 12:07:11 +020062#ifdef HAVE_ARCH_X86_64_SUPPORT
Ingo Molnar13839ec2015-10-19 10:04:17 +020063# define MEMCPY_FN(_fn, _name, _desc) {.name = _name, .desc = _desc, .fn.memcpy = _fn},
64# include "mem-memcpy-x86-64-asm-def.h"
65# undef MEMCPY_FN
Hitoshi Mitake49ce8fc2010-11-25 16:04:52 +090066#endif
67
Ingo Molnar13839ec2015-10-19 10:04:17 +020068 { NULL, }
Hitoshi Mitake827f3b42009-11-18 00:20:09 +090069};
70
71static const char * const bench_mem_memcpy_usage[] = {
72 "perf bench mem memcpy <options>",
73 NULL
74};
75
Hitoshi Mitake17d7a112012-07-02 22:46:17 +090076static struct perf_event_attr cycle_attr = {
Hitoshi Mitake12eac0b2009-11-20 12:37:17 +090077 .type = PERF_TYPE_HARDWARE,
78 .config = PERF_COUNT_HW_CPU_CYCLES
Hitoshi Mitake827f3b42009-11-18 00:20:09 +090079};
80
Hitoshi Mitake17d7a112012-07-02 22:46:17 +090081static void init_cycle(void)
Hitoshi Mitake827f3b42009-11-18 00:20:09 +090082{
Ingo Molnar13839ec2015-10-19 10:04:17 +020083 cycle_fd = sys_perf_event_open(&cycle_attr, getpid(), -1, -1, perf_event_open_cloexec_flag());
Hitoshi Mitake12eac0b2009-11-20 12:37:17 +090084
Hitoshi Mitake17d7a112012-07-02 22:46:17 +090085 if (cycle_fd < 0 && errno == ENOSYS)
Hitoshi Mitake12eac0b2009-11-20 12:37:17 +090086 die("No CONFIG_PERF_EVENTS=y kernel support configured?\n");
87 else
Hitoshi Mitake17d7a112012-07-02 22:46:17 +090088 BUG_ON(cycle_fd < 0);
Hitoshi Mitake827f3b42009-11-18 00:20:09 +090089}
90
Hitoshi Mitake17d7a112012-07-02 22:46:17 +090091static u64 get_cycle(void)
Hitoshi Mitake827f3b42009-11-18 00:20:09 +090092{
93 int ret;
94 u64 clk;
95
Hitoshi Mitake17d7a112012-07-02 22:46:17 +090096 ret = read(cycle_fd, &clk, sizeof(u64));
Hitoshi Mitake827f3b42009-11-18 00:20:09 +090097 BUG_ON(ret != sizeof(u64));
98
99 return clk;
100}
101
102static double timeval2double(struct timeval *ts)
103{
Ingo Molnar13839ec2015-10-19 10:04:17 +0200104 return (double)ts->tv_sec + (double)ts->tv_usec / (double)1000000;
Hitoshi Mitake827f3b42009-11-18 00:20:09 +0900105}
106
Ingo Molnar6db175c2015-10-19 10:04:21 +0200107#define print_bps(x) do { \
108 if (x < K) \
109 printf(" %14lf B/Sec\n", x); \
110 else if (x < K * K) \
111 printf(" %14lfd KB/Sec\n", x / K); \
112 else if (x < K * K * K) \
113 printf(" %14lf MB/Sec\n", x / K / K); \
114 else \
115 printf(" %14lf GB/Sec\n", x / K / K / K); \
Hitoshi Mitake49ce8fc2010-11-25 16:04:52 +0900116 } while (0)
117
Rabin Vincent308197b2014-12-02 16:50:39 +0100118struct bench_mem_info {
119 const struct routine *routines;
Ingo Molnar6db175c2015-10-19 10:04:21 +0200120 u64 (*do_cycle)(const struct routine *r, size_t len);
121 double (*do_gettimeofday)(const struct routine *r, size_t len);
Rabin Vincent308197b2014-12-02 16:50:39 +0100122 const char *const *usage;
123};
124
Borislav Petkov515e23f2015-02-26 18:51:37 +0100125static void __bench_mem_routine(struct bench_mem_info *info, int r_idx, size_t len, double totallen)
Hitoshi Mitake827f3b42009-11-18 00:20:09 +0900126{
Borislav Petkov515e23f2015-02-26 18:51:37 +0100127 const struct routine *r = &info->routines[r_idx];
Ingo Molnar6db175c2015-10-19 10:04:21 +0200128 double result_bps = 0.0;
129 u64 result_cycle = 0;
Hitoshi Mitake49ce8fc2010-11-25 16:04:52 +0900130
Borislav Petkovdfecb952015-02-26 19:02:43 +0100131 printf("Routine %s (%s)\n", r->name, r->desc);
Hitoshi Mitake827f3b42009-11-18 00:20:09 +0900132
Hitoshi Mitake49ce8fc2010-11-25 16:04:52 +0900133 if (bench_format == BENCH_FORMAT_DEFAULT)
134 printf("# Copying %s Bytes ...\n\n", length_str);
Hitoshi Mitake12eac0b2009-11-20 12:37:17 +0900135
Ingo Molnar6db175c2015-10-19 10:04:21 +0200136 if (use_cycle) {
137 result_cycle = info->do_cycle(r, len);
Hitoshi Mitake12eac0b2009-11-20 12:37:17 +0900138 } else {
Ingo Molnar6db175c2015-10-19 10:04:21 +0200139 result_bps = info->do_gettimeofday(r, len);
Hitoshi Mitake827f3b42009-11-18 00:20:09 +0900140 }
141
142 switch (bench_format) {
143 case BENCH_FORMAT_DEFAULT:
Ingo Molnar6db175c2015-10-19 10:04:21 +0200144 if (use_cycle) {
145 printf(" %14lf Cycle/Byte\n", (double)result_cycle/totallen);
Hitoshi Mitake49ce8fc2010-11-25 16:04:52 +0900146 } else {
Ingo Molnar6db175c2015-10-19 10:04:21 +0200147 print_bps(result_bps);
148 }
149 break;
Hitoshi Mitake49ce8fc2010-11-25 16:04:52 +0900150
Hitoshi Mitake827f3b42009-11-18 00:20:09 +0900151 case BENCH_FORMAT_SIMPLE:
Ingo Molnar6db175c2015-10-19 10:04:21 +0200152 if (use_cycle) {
153 printf("%lf\n", (double)result_cycle/totallen);
Hitoshi Mitake49ce8fc2010-11-25 16:04:52 +0900154 } else {
Ingo Molnar6db175c2015-10-19 10:04:21 +0200155 printf("%lf\n", result_bps);
Hitoshi Mitake49ce8fc2010-11-25 16:04:52 +0900156 }
Hitoshi Mitake827f3b42009-11-18 00:20:09 +0900157 break;
Ingo Molnar6db175c2015-10-19 10:04:21 +0200158
Hitoshi Mitake827f3b42009-11-18 00:20:09 +0900159 default:
Ingo Molnar6db175c2015-10-19 10:04:21 +0200160 BUG_ON(1);
Hitoshi Mitake827f3b42009-11-18 00:20:09 +0900161 break;
162 }
Borislav Petkov515e23f2015-02-26 18:51:37 +0100163}
164
Ingo Molnar2946f592015-10-19 10:04:19 +0200165static int bench_mem_common(int argc, const char **argv, struct bench_mem_info *info)
Borislav Petkov515e23f2015-02-26 18:51:37 +0100166{
167 int i;
168 size_t len;
169 double totallen;
170
Ingo Molnar13839ec2015-10-19 10:04:17 +0200171 argc = parse_options(argc, argv, options, info->usage, 0);
Borislav Petkov515e23f2015-02-26 18:51:37 +0100172
Borislav Petkov515e23f2015-02-26 18:51:37 +0100173 if (use_cycle)
174 init_cycle();
175
176 len = (size_t)perf_atoll((char *)length_str);
177 totallen = (double)len * iterations;
178
179 if ((s64)len <= 0) {
180 fprintf(stderr, "Invalid length:%s\n", length_str);
181 return 1;
182 }
183
Borislav Petkovdfecb952015-02-26 19:02:43 +0100184 if (!strncmp(routine, "all", 3)) {
185 for (i = 0; info->routines[i].name; i++)
186 __bench_mem_routine(info, i, len, totallen);
187 return 0;
188 }
189
Borislav Petkov515e23f2015-02-26 18:51:37 +0100190 for (i = 0; info->routines[i].name; i++) {
191 if (!strcmp(info->routines[i].name, routine))
192 break;
193 }
194 if (!info->routines[i].name) {
195 printf("Unknown routine:%s\n", routine);
196 printf("Available routines...\n");
197 for (i = 0; info->routines[i].name; i++) {
198 printf("\t%s ... %s\n",
199 info->routines[i].name, info->routines[i].desc);
200 }
201 return 1;
202 }
203
204 __bench_mem_routine(info, i, len, totallen);
Hitoshi Mitake827f3b42009-11-18 00:20:09 +0900205
206 return 0;
207}
Rabin Vincent308197b2014-12-02 16:50:39 +0100208
209static void memcpy_alloc_mem(void **dst, void **src, size_t length)
210{
211 *dst = zalloc(length);
212 if (!*dst)
213 die("memory allocation failed - maybe length is too large?\n");
214
215 *src = zalloc(length);
216 if (!*src)
217 die("memory allocation failed - maybe length is too large?\n");
Ingo Molnar13839ec2015-10-19 10:04:17 +0200218
219 /* Make sure to always prefault zero pages even if MMAP_THRESH is crossed: */
Rabin Vincent308197b2014-12-02 16:50:39 +0100220 memset(*src, 0, length);
221}
222
Ingo Molnar6db175c2015-10-19 10:04:21 +0200223static u64 do_memcpy_cycle(const struct routine *r, size_t len)
Rabin Vincent308197b2014-12-02 16:50:39 +0100224{
225 u64 cycle_start = 0ULL, cycle_end = 0ULL;
226 void *src = NULL, *dst = NULL;
227 memcpy_t fn = r->fn.memcpy;
228 int i;
229
Bruce Merrye17fdae2015-01-15 11:20:22 +0200230 memcpy_alloc_mem(&dst, &src, len);
Rabin Vincent308197b2014-12-02 16:50:39 +0100231
Ingo Molnar6db175c2015-10-19 10:04:21 +0200232 /*
233 * We prefault the freshly allocated memory range here,
234 * to not measure page fault overhead:
235 */
236 fn(dst, src, len);
Rabin Vincent308197b2014-12-02 16:50:39 +0100237
238 cycle_start = get_cycle();
239 for (i = 0; i < iterations; ++i)
240 fn(dst, src, len);
241 cycle_end = get_cycle();
242
243 free(src);
244 free(dst);
245 return cycle_end - cycle_start;
246}
247
Ingo Molnar6db175c2015-10-19 10:04:21 +0200248static double do_memcpy_gettimeofday(const struct routine *r, size_t len)
Rabin Vincent308197b2014-12-02 16:50:39 +0100249{
250 struct timeval tv_start, tv_end, tv_diff;
251 memcpy_t fn = r->fn.memcpy;
252 void *src = NULL, *dst = NULL;
253 int i;
254
Bruce Merrye17fdae2015-01-15 11:20:22 +0200255 memcpy_alloc_mem(&dst, &src, len);
Rabin Vincent308197b2014-12-02 16:50:39 +0100256
Ingo Molnar6db175c2015-10-19 10:04:21 +0200257 /*
258 * We prefault the freshly allocated memory range here,
259 * to not measure page fault overhead:
260 */
261 fn(dst, src, len);
Rabin Vincent308197b2014-12-02 16:50:39 +0100262
263 BUG_ON(gettimeofday(&tv_start, NULL));
264 for (i = 0; i < iterations; ++i)
265 fn(dst, src, len);
266 BUG_ON(gettimeofday(&tv_end, NULL));
267
268 timersub(&tv_end, &tv_start, &tv_diff);
269
270 free(src);
271 free(dst);
Ingo Molnar6db175c2015-10-19 10:04:21 +0200272
Rabin Vincent1182f882014-12-02 16:50:41 +0100273 return (double)(((double)len * iterations) / timeval2double(&tv_diff));
Rabin Vincent308197b2014-12-02 16:50:39 +0100274}
275
Ingo Molnar2946f592015-10-19 10:04:19 +0200276int bench_mem_memcpy(int argc, const char **argv, const char *prefix __maybe_unused)
Rabin Vincent308197b2014-12-02 16:50:39 +0100277{
278 struct bench_mem_info info = {
Ingo Molnar13839ec2015-10-19 10:04:17 +0200279 .routines = memcpy_routines,
280 .do_cycle = do_memcpy_cycle,
281 .do_gettimeofday = do_memcpy_gettimeofday,
282 .usage = bench_mem_memcpy_usage,
Rabin Vincent308197b2014-12-02 16:50:39 +0100283 };
284
Ingo Molnar2946f592015-10-19 10:04:19 +0200285 return bench_mem_common(argc, argv, &info);
Rabin Vincent308197b2014-12-02 16:50:39 +0100286}
Rabin Vincent5bce1a52014-12-02 16:50:40 +0100287
288static void memset_alloc_mem(void **dst, size_t length)
289{
290 *dst = zalloc(length);
291 if (!*dst)
292 die("memory allocation failed - maybe length is too large?\n");
293}
294
Ingo Molnar6db175c2015-10-19 10:04:21 +0200295static u64 do_memset_cycle(const struct routine *r, size_t len)
Rabin Vincent5bce1a52014-12-02 16:50:40 +0100296{
297 u64 cycle_start = 0ULL, cycle_end = 0ULL;
298 memset_t fn = r->fn.memset;
299 void *dst = NULL;
300 int i;
301
302 memset_alloc_mem(&dst, len);
303
Ingo Molnar6db175c2015-10-19 10:04:21 +0200304 /*
305 * We prefault the freshly allocated memory range here,
306 * to not measure page fault overhead:
307 */
308 fn(dst, -1, len);
Rabin Vincent5bce1a52014-12-02 16:50:40 +0100309
310 cycle_start = get_cycle();
311 for (i = 0; i < iterations; ++i)
312 fn(dst, i, len);
313 cycle_end = get_cycle();
314
315 free(dst);
316 return cycle_end - cycle_start;
317}
318
Ingo Molnar6db175c2015-10-19 10:04:21 +0200319static double do_memset_gettimeofday(const struct routine *r, size_t len)
Rabin Vincent5bce1a52014-12-02 16:50:40 +0100320{
321 struct timeval tv_start, tv_end, tv_diff;
322 memset_t fn = r->fn.memset;
323 void *dst = NULL;
324 int i;
325
326 memset_alloc_mem(&dst, len);
327
Ingo Molnar6db175c2015-10-19 10:04:21 +0200328 /*
329 * We prefault the freshly allocated memory range here,
330 * to not measure page fault overhead:
331 */
332 fn(dst, -1, len);
Rabin Vincent5bce1a52014-12-02 16:50:40 +0100333
334 BUG_ON(gettimeofday(&tv_start, NULL));
335 for (i = 0; i < iterations; ++i)
336 fn(dst, i, len);
337 BUG_ON(gettimeofday(&tv_end, NULL));
338
339 timersub(&tv_end, &tv_start, &tv_diff);
340
341 free(dst);
Rabin Vincent1182f882014-12-02 16:50:41 +0100342 return (double)(((double)len * iterations) / timeval2double(&tv_diff));
Rabin Vincent5bce1a52014-12-02 16:50:40 +0100343}
344
345static const char * const bench_mem_memset_usage[] = {
346 "perf bench mem memset <options>",
347 NULL
348};
349
350static const struct routine memset_routines[] = {
Ingo Molnar13839ec2015-10-19 10:04:17 +0200351 { .name = "default",
352 .desc = "Default memset() provided by glibc",
353 .fn.memset = memset },
354
Rabin Vincent5bce1a52014-12-02 16:50:40 +0100355#ifdef HAVE_ARCH_X86_64_SUPPORT
Ingo Molnar13839ec2015-10-19 10:04:17 +0200356# define MEMSET_FN(_fn, _name, _desc) { .name = _name, .desc = _desc, .fn.memset = _fn },
357# include "mem-memset-x86-64-asm-def.h"
358# undef MEMSET_FN
Rabin Vincent5bce1a52014-12-02 16:50:40 +0100359#endif
360
Ingo Molnar13839ec2015-10-19 10:04:17 +0200361 { NULL, }
Rabin Vincent5bce1a52014-12-02 16:50:40 +0100362};
363
Ingo Molnar13839ec2015-10-19 10:04:17 +0200364int bench_mem_memset(int argc, const char **argv, const char *prefix __maybe_unused)
Rabin Vincent5bce1a52014-12-02 16:50:40 +0100365{
366 struct bench_mem_info info = {
Ingo Molnar13839ec2015-10-19 10:04:17 +0200367 .routines = memset_routines,
368 .do_cycle = do_memset_cycle,
369 .do_gettimeofday = do_memset_gettimeofday,
370 .usage = bench_mem_memset_usage,
Rabin Vincent5bce1a52014-12-02 16:50:40 +0100371 };
372
Ingo Molnar2946f592015-10-19 10:04:19 +0200373 return bench_mem_common(argc, argv, &info);
Rabin Vincent5bce1a52014-12-02 16:50:40 +0100374}