xref: /xnu-10002.81.5/tests/vm/perf_madvise.c (revision 5e3eaea39dcf651e66cb99ba7d70e32cc4a99587)
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