Commit | Line | Data |
---|---|---|
827f3b49 HM |
1 | /* |
2 | * mem-memcpy.c | |
3 | * | |
4 | * memcpy: Simple memory copy in various ways | |
5 | * | |
6 | * Written by Hitoshi Mitake <mitake@dcl.info.waseda.ac.jp> | |
7 | */ | |
8 | #include <ctype.h> | |
9 | ||
10 | #include "../perf.h" | |
11 | #include "../util/util.h" | |
12 | #include "../util/parse-options.h" | |
827f3b49 HM |
13 | #include "../util/header.h" |
14 | #include "bench.h" | |
15 | ||
16 | #include <stdio.h> | |
17 | #include <stdlib.h> | |
18 | #include <string.h> | |
19 | #include <sys/time.h> | |
20 | #include <errno.h> | |
21 | ||
22 | #define K 1024 | |
23 | ||
12eac0bf HM |
24 | static const char *length_str = "1MB"; |
25 | static const char *routine = "default"; | |
c0555642 | 26 | static bool use_clock = false; |
12eac0bf | 27 | static int clock_fd; |
827f3b49 HM |
28 | |
29 | static const struct option options[] = { | |
30 | OPT_STRING('l', "length", &length_str, "1MB", | |
31 | "Specify length of memory to copy. " | |
32 | "available unit: B, MB, GB (upper and lower)"), | |
33 | OPT_STRING('r', "routine", &routine, "default", | |
34 | "Specify routine to copy"), | |
35 | OPT_BOOLEAN('c', "clock", &use_clock, | |
36 | "Use CPU clock for measuring"), | |
37 | OPT_END() | |
38 | }; | |
39 | ||
40 | struct routine { | |
41 | const char *name; | |
42 | const char *desc; | |
43 | void * (*fn)(void *dst, const void *src, size_t len); | |
44 | }; | |
45 | ||
46 | struct routine routines[] = { | |
47 | { "default", | |
48 | "Default memcpy() provided by glibc", | |
49 | memcpy }, | |
50 | { NULL, | |
51 | NULL, | |
52 | NULL } | |
53 | }; | |
54 | ||
55 | static const char * const bench_mem_memcpy_usage[] = { | |
56 | "perf bench mem memcpy <options>", | |
57 | NULL | |
58 | }; | |
59 | ||
827f3b49 | 60 | static struct perf_event_attr clock_attr = { |
12eac0bf HM |
61 | .type = PERF_TYPE_HARDWARE, |
62 | .config = PERF_COUNT_HW_CPU_CYCLES | |
827f3b49 HM |
63 | }; |
64 | ||
65 | static void init_clock(void) | |
66 | { | |
67 | clock_fd = sys_perf_event_open(&clock_attr, getpid(), -1, -1, 0); | |
12eac0bf HM |
68 | |
69 | if (clock_fd < 0 && errno == ENOSYS) | |
70 | die("No CONFIG_PERF_EVENTS=y kernel support configured?\n"); | |
71 | else | |
72 | BUG_ON(clock_fd < 0); | |
827f3b49 HM |
73 | } |
74 | ||
75 | static u64 get_clock(void) | |
76 | { | |
77 | int ret; | |
78 | u64 clk; | |
79 | ||
80 | ret = read(clock_fd, &clk, sizeof(u64)); | |
81 | BUG_ON(ret != sizeof(u64)); | |
82 | ||
83 | return clk; | |
84 | } | |
85 | ||
86 | static double timeval2double(struct timeval *ts) | |
87 | { | |
88 | return (double)ts->tv_sec + | |
89 | (double)ts->tv_usec / (double)1000000; | |
90 | } | |
91 | ||
92 | int bench_mem_memcpy(int argc, const char **argv, | |
93 | const char *prefix __used) | |
94 | { | |
95 | int i; | |
96 | void *dst, *src; | |
97 | size_t length; | |
98 | double bps = 0.0; | |
99 | struct timeval tv_start, tv_end, tv_diff; | |
100 | u64 clock_start, clock_end, clock_diff; | |
101 | ||
102 | clock_start = clock_end = clock_diff = 0ULL; | |
103 | argc = parse_options(argc, argv, options, | |
104 | bench_mem_memcpy_usage, 0); | |
105 | ||
106 | tv_diff.tv_sec = 0; | |
107 | tv_diff.tv_usec = 0; | |
108 | length = (size_t)perf_atoll((char *)length_str); | |
12eac0bf HM |
109 | |
110 | if ((s64)length <= 0) { | |
827f3b49 HM |
111 | fprintf(stderr, "Invalid length:%s\n", length_str); |
112 | return 1; | |
113 | } | |
114 | ||
115 | for (i = 0; routines[i].name; i++) { | |
116 | if (!strcmp(routines[i].name, routine)) | |
117 | break; | |
118 | } | |
119 | if (!routines[i].name) { | |
120 | printf("Unknown routine:%s\n", routine); | |
121 | printf("Available routines...\n"); | |
122 | for (i = 0; routines[i].name; i++) { | |
123 | printf("\t%s ... %s\n", | |
124 | routines[i].name, routines[i].desc); | |
125 | } | |
126 | return 1; | |
127 | } | |
128 | ||
36479484 | 129 | dst = zalloc(length); |
12eac0bf HM |
130 | if (!dst) |
131 | die("memory allocation failed - maybe length is too large?\n"); | |
132 | ||
36479484 | 133 | src = zalloc(length); |
12eac0bf HM |
134 | if (!src) |
135 | die("memory allocation failed - maybe length is too large?\n"); | |
827f3b49 HM |
136 | |
137 | if (bench_format == BENCH_FORMAT_DEFAULT) { | |
138 | printf("# Copying %s Bytes from %p to %p ...\n\n", | |
139 | length_str, src, dst); | |
140 | } | |
141 | ||
142 | if (use_clock) { | |
143 | init_clock(); | |
144 | clock_start = get_clock(); | |
12eac0bf | 145 | } else { |
827f3b49 | 146 | BUG_ON(gettimeofday(&tv_start, NULL)); |
12eac0bf | 147 | } |
827f3b49 HM |
148 | |
149 | routines[i].fn(dst, src, length); | |
150 | ||
151 | if (use_clock) { | |
152 | clock_end = get_clock(); | |
153 | clock_diff = clock_end - clock_start; | |
154 | } else { | |
155 | BUG_ON(gettimeofday(&tv_end, NULL)); | |
156 | timersub(&tv_end, &tv_start, &tv_diff); | |
157 | bps = (double)((double)length / timeval2double(&tv_diff)); | |
158 | } | |
159 | ||
160 | switch (bench_format) { | |
161 | case BENCH_FORMAT_DEFAULT: | |
162 | if (use_clock) { | |
163 | printf(" %14lf Clock/Byte\n", | |
164 | (double)clock_diff / (double)length); | |
165 | } else { | |
166 | if (bps < K) | |
167 | printf(" %14lf B/Sec\n", bps); | |
168 | else if (bps < K * K) | |
169 | printf(" %14lfd KB/Sec\n", bps / 1024); | |
170 | else if (bps < K * K * K) | |
171 | printf(" %14lf MB/Sec\n", bps / 1024 / 1024); | |
172 | else { | |
173 | printf(" %14lf GB/Sec\n", | |
174 | bps / 1024 / 1024 / 1024); | |
175 | } | |
176 | } | |
177 | break; | |
178 | case BENCH_FORMAT_SIMPLE: | |
179 | if (use_clock) { | |
180 | printf("%14lf\n", | |
181 | (double)clock_diff / (double)length); | |
182 | } else | |
183 | printf("%lf\n", bps); | |
184 | break; | |
185 | default: | |
12eac0bf HM |
186 | /* reaching this means there's some disaster: */ |
187 | die("unknown format: %d\n", bench_format); | |
827f3b49 HM |
188 | break; |
189 | } | |
190 | ||
191 | return 0; | |
192 | } |