1 /*
2 * Madvise benchmark.
3 * Currently only times various types of madvise frees.
4 */
5
6 #include <assert.h>
7 #include <errno.h>
8 #include <stdio.h>
9 #include <stdlib.h>
10 #include <string.h>
11
12 #include <sys/mman.h>
13 #include <sys/sysctl.h>
14
15 #include "benchmark/helpers.h"
16
17 typedef enum test_variant {
18 VARIANT_MADVISE_FREE
19 } test_variant_t;
20
21 /* Arguments parsed from the command line */
22 typedef struct test_args {
23 uint64_t ta_duration_seconds;
24 uint64_t ta_size;
25 test_variant_t ta_variant;
26 bool ta_verbose;
27 } test_args_t;
28
29 typedef struct test_result {
30 double tr_madvise_throughput;
31 double tr_faultback_throughput;
32 } test_result_t;
33
34 static void print_help(char **argv);
35 static void parse_arguments(int argc, char** argv, test_args_t *args);
36 static test_result_t madvise_free_test(const test_args_t* args);
37 /*
38 * Allocate a buffer of the given size and fault in all of its pages.
39 */
40 static void *allocate_and_init_buffer(uint64_t size);
41 /*
42 * Fault in the pages in the given buffer.
43 */
44 static void fault_pages(unsigned char *buffer, size_t size, size_t stride);
45 /*
46 * Output the results of the test in pages / CPU second.
47 */
48 static void output_throughput(test_result_t tr);
49
50 /* Test Variants */
51 static const char* kMadviseFreeArgument = "MADV_FREE";
52 /* The VM page size */
53 static size_t kPageSize = 0;
54 static const clockid_t kThreadCPUTimeClock = CLOCK_THREAD_CPUTIME_ID;
55
56 int
main(int argc,char ** argv)57 main(int argc, char** argv)
58 {
59 test_args_t args;
60 parse_arguments(argc, argv, &args);
61 test_result_t tr;
62 if (args.ta_variant == VARIANT_MADVISE_FREE) {
63 tr = madvise_free_test(&args);
64 } else {
65 fprintf(stderr, "Unknown test variant\n");
66 exit(2);
67 }
68 output_throughput(tr);
69 return 0;
70 }
71
72 static test_result_t
madvise_free_test(const test_args_t * args)73 madvise_free_test(const test_args_t* args)
74 {
75 int ret, ret_end;
76 assert(args->ta_variant == VARIANT_MADVISE_FREE);
77 benchmark_log(args->ta_verbose, "Running madvise free test\n");
78 size_t time_elapsed_us = 0;
79 size_t time_fault_us = 0;
80 size_t count = 0;
81
82 test_result_t tr;
83
84 while (time_elapsed_us < args->ta_duration_seconds * kNumMicrosecondsInSecond) {
85 benchmark_log(args->ta_verbose, "Starting iteration %zu\n", count + 1);
86 void* buffer = allocate_and_init_buffer(args->ta_size);
87 benchmark_log(args->ta_verbose, "Allocated and faulted in test buffer\n");
88 struct timespec start_time, end_time;
89 ret = clock_gettime(kThreadCPUTimeClock, &start_time);
90
91 madvise(buffer, args->ta_size, MADV_FREE);
92
93 ret_end = clock_gettime(kThreadCPUTimeClock, &end_time);
94 assert(ret == 0);
95 assert(ret_end == 0);
96
97 time_elapsed_us += timespec_difference_us(&end_time, &start_time);
98
99 ret = clock_gettime(kThreadCPUTimeClock, &start_time);
100
101 fault_pages(buffer, args->ta_size, kPageSize);
102
103 ret_end = clock_gettime(kThreadCPUTimeClock, &end_time);
104 assert(ret == 0);
105 assert(ret_end == 0);
106 time_fault_us += timespec_difference_us(&end_time, &start_time);
107
108 ret = munmap(buffer, args->ta_size);
109 assert(ret == 0);
110 benchmark_log(args->ta_verbose, "Completed iteration %zu\nMeasured %zu time on CPU so far.\n", count + 1, time_elapsed_us);
111
112 count++;
113 }
114 assert(kPageSize != 0);
115 tr.tr_madvise_throughput = (count * args->ta_size) / ((double)time_elapsed_us / kNumMicrosecondsInSecond);
116 tr.tr_faultback_throughput = (count * args->ta_size) / ((double)time_fault_us / kNumMicrosecondsInSecond);
117
118 return tr;
119 }
120
121 static void *
allocate_and_init_buffer(uint64_t size)122 allocate_and_init_buffer(uint64_t size)
123 {
124 unsigned char *buffer = NULL;
125 int ret;
126 size_t len;
127 if (kPageSize == 0) {
128 size_t pagesize_size = sizeof(kPageSize);
129 ret = sysctlbyname("vm.pagesize", &kPageSize, &pagesize_size, NULL, 0);
130 assert(ret == 0);
131 assert(kPageSize > 0);
132 }
133 len = size;
134 buffer = mmap_buffer(len);
135 fault_pages(buffer, len, kPageSize);
136 return buffer;
137 }
138
139 static void
fault_pages(unsigned char * buffer,size_t size,size_t stride)140 fault_pages(unsigned char *buffer, size_t size, size_t stride)
141 {
142 volatile unsigned char val;
143 for (unsigned char* ptr = buffer; ptr < buffer + size; ptr += stride) {
144 val = *ptr;
145 }
146 }
147
148 static void
parse_arguments(int argc,char ** argv,test_args_t * args)149 parse_arguments(int argc, char** argv, test_args_t *args)
150 {
151 int current_positional_argument = 0;
152 long duration = -1, size_mb = -1;
153 memset(args, 0, sizeof(test_args_t));
154 for (int current_argument = 1; current_argument < argc; current_argument++) {
155 if (argv[current_argument][0] == '-') {
156 if (strcmp(argv[current_argument], "-v") == 0) {
157 args->ta_verbose = true;
158 } else {
159 fprintf(stderr, "Unknown argument %s\n", argv[current_argument]);
160 print_help(argv);
161 exit(1);
162 }
163 if (current_argument >= argc) {
164 print_help(argv);
165 exit(1);
166 }
167 } else {
168 if (current_positional_argument == 0) {
169 if (strcasecmp(argv[current_argument], kMadviseFreeArgument) == 0) {
170 args->ta_variant = VARIANT_MADVISE_FREE;
171 } else {
172 print_help(argv);
173 exit(1);
174 }
175 current_positional_argument++;
176 } else if (current_positional_argument == 1) {
177 duration = strtol(argv[current_argument], NULL, 10);
178 if (duration <= 0) {
179 print_help(argv);
180 exit(1);
181 }
182 current_positional_argument++;
183 } else if (current_positional_argument == 2) {
184 size_mb = strtol(argv[current_argument], NULL, 10);
185 if (size_mb <= 0) {
186 print_help(argv);
187 exit(1);
188 }
189 current_positional_argument++;
190 } else {
191 print_help(argv);
192 exit(1);
193 }
194 }
195 }
196 if (current_positional_argument != 3) {
197 fprintf(stderr, "Expected 3 positional arguments. %d were supplied.\n", current_positional_argument);
198 print_help(argv);
199 exit(1);
200 }
201 args->ta_duration_seconds = (uint64_t) duration;
202 args->ta_size = ((uint64_t) size_mb * (1UL << 20));
203 }
204
205 static void
print_help(char ** argv)206 print_help(char** argv)
207 {
208 fprintf(stderr, "%s: <test-variant> [-v] duration_seconds size_mb\n", argv[0]);
209 fprintf(stderr, "\ntest variants:\n");
210 fprintf(stderr, " %s Measure MADV_FREE time.\n", kMadviseFreeArgument);
211 }
212
213 static void
output_throughput(test_result_t tr)214 output_throughput(test_result_t tr)
215 {
216 printf("-----Results-----\n");
217 printf("madvise throughput,bytes / CPU second,");
218 printf("%f\n", tr.tr_madvise_throughput);
219 printf("fault back throughput,bytes / CPU second,");
220 printf("%f\n", tr.tr_faultback_throughput);
221 }
222