xref: /xnu-8792.61.2/tests/vm/perf_madvise.c (revision 42e220869062b56f8d7d0726fd4c88954f87902c)
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 static void print_help(char **argv);
30 static void parse_arguments(int argc, char** argv, test_args_t *args);
31 static double madvise_free_test(const test_args_t* args);
32 /*
33  * Allocate a buffer of the given size and fault in all of its pages.
34  */
35 static void *allocate_and_init_buffer(uint64_t size);
36 /*
37  * Fault in the pages in the given buffer.
38  */
39 static void fault_pages(unsigned char *buffer, size_t size, size_t stride);
40 /*
41  * Output the results of the test in pages / CPU second.
42  */
43 static void output_throughput(double throughput);
44 
45 /* Test Variants */
46 static const char* kMadviseFreeArgument = "MADV_FREE";
47 /* The VM page size */
48 static size_t kPageSize = 0;
49 static const clockid_t kThreadCPUTimeClock = CLOCK_THREAD_CPUTIME_ID;
50 
51 int
main(int argc,char ** argv)52 main(int argc, char** argv)
53 {
54 	test_args_t args;
55 	parse_arguments(argc, argv, &args);
56 	double throughput = 0.0;
57 	if (args.ta_variant == VARIANT_MADVISE_FREE) {
58 		throughput = madvise_free_test(&args);
59 	} else {
60 		fprintf(stderr, "Unknown test variant\n");
61 		exit(2);
62 	}
63 	output_throughput(throughput);
64 	return 0;
65 }
66 
67 static double
madvise_free_test(const test_args_t * args)68 madvise_free_test(const test_args_t* args)
69 {
70 	int ret, ret_end;
71 	assert(args->ta_variant == VARIANT_MADVISE_FREE);
72 	benchmark_log(args->ta_verbose, "Running madvise free test\n");
73 	size_t time_elapsed_us = 0;
74 	size_t count = 0;
75 	double throughput = 0;
76 
77 	while (time_elapsed_us < args->ta_duration_seconds * kNumMicrosecondsInSecond) {
78 		benchmark_log(args->ta_verbose, "Starting iteration %zu\n", count + 1);
79 		void* buffer = allocate_and_init_buffer(args->ta_size);
80 		benchmark_log(args->ta_verbose, "Allocated and faulted in test buffer\n");
81 		struct timespec start_time, end_time;
82 		ret = clock_gettime(kThreadCPUTimeClock, &start_time);
83 
84 		madvise(buffer, args->ta_size, MADV_FREE);
85 
86 		ret_end = clock_gettime(kThreadCPUTimeClock, &end_time);
87 		assert(ret == 0);
88 		assert(ret_end == 0);
89 		time_elapsed_us += timespec_difference_us(&end_time, &start_time);
90 
91 		ret = munmap(buffer, args->ta_size);
92 		assert(ret == 0);
93 		benchmark_log(args->ta_verbose, "Completed iteration %zu\nMeasured %zu time on CPU so far.\n", count + 1, time_elapsed_us);
94 
95 		count++;
96 	}
97 	assert(kPageSize != 0);
98 	throughput = (count * args->ta_size) / ((double)time_elapsed_us / kNumMicrosecondsInSecond);
99 	return throughput;
100 }
101 
102 static void *
allocate_and_init_buffer(uint64_t size)103 allocate_and_init_buffer(uint64_t size)
104 {
105 	unsigned char *buffer = NULL;
106 	int ret;
107 	size_t len;
108 	if (kPageSize == 0) {
109 		size_t pagesize_size = sizeof(kPageSize);
110 		ret = sysctlbyname("vm.pagesize", &kPageSize, &pagesize_size, NULL, 0);
111 		assert(ret == 0);
112 		assert(kPageSize > 0);
113 	}
114 	len = size;
115 	buffer = mmap_buffer(len);
116 	fault_pages(buffer, len, kPageSize);
117 	return buffer;
118 }
119 
120 static void
fault_pages(unsigned char * buffer,size_t size,size_t stride)121 fault_pages(unsigned char *buffer, size_t size, size_t stride)
122 {
123 	volatile unsigned char val;
124 	for (unsigned char* ptr = buffer; ptr < buffer + size; ptr += stride) {
125 		val = *ptr;
126 	}
127 }
128 
129 static void
parse_arguments(int argc,char ** argv,test_args_t * args)130 parse_arguments(int argc, char** argv, test_args_t *args)
131 {
132 	int current_positional_argument = 0;
133 	long duration = -1, size_mb = -1;
134 	memset(args, 0, sizeof(test_args_t));
135 	for (int current_argument = 1; current_argument < argc; current_argument++) {
136 		if (argv[current_argument][0] == '-') {
137 			if (strcmp(argv[current_argument], "-v") == 0) {
138 				args->ta_verbose = true;
139 			} else {
140 				fprintf(stderr, "Unknown argument %s\n", argv[current_argument]);
141 				print_help(argv);
142 				exit(1);
143 			}
144 			if (current_argument >= argc) {
145 				print_help(argv);
146 				exit(1);
147 			}
148 		} else {
149 			if (current_positional_argument == 0) {
150 				if (strcasecmp(argv[current_argument], kMadviseFreeArgument) == 0) {
151 					args->ta_variant = VARIANT_MADVISE_FREE;
152 				} else {
153 					print_help(argv);
154 					exit(1);
155 				}
156 				current_positional_argument++;
157 			} else if (current_positional_argument == 1) {
158 				duration = strtol(argv[current_argument], NULL, 10);
159 				if (duration <= 0) {
160 					print_help(argv);
161 					exit(1);
162 				}
163 				current_positional_argument++;
164 			} else if (current_positional_argument == 2) {
165 				size_mb = strtol(argv[current_argument], NULL, 10);
166 				if (size_mb <= 0) {
167 					print_help(argv);
168 					exit(1);
169 				}
170 				current_positional_argument++;
171 			} else {
172 				print_help(argv);
173 				exit(1);
174 			}
175 		}
176 	}
177 	if (current_positional_argument != 3) {
178 		fprintf(stderr, "Expected 3 positional arguments. %d were supplied.\n", current_positional_argument);
179 		print_help(argv);
180 		exit(1);
181 	}
182 	args->ta_duration_seconds = (uint64_t) duration;
183 	args->ta_size = ((uint64_t) size_mb * (1UL << 20));
184 }
185 
186 static void
print_help(char ** argv)187 print_help(char** argv)
188 {
189 	fprintf(stderr, "%s: <test-variant> [-v] duration_seconds size_mb\n", argv[0]);
190 	fprintf(stderr, "\ntest variants:\n");
191 	fprintf(stderr, "	%s	Measure MADV_FREE time.\n", kMadviseFreeArgument);
192 }
193 
194 static void
output_throughput(double throughput)195 output_throughput(double throughput)
196 {
197 	printf("-----Results-----\n");
198 	printf("Throughput (bytes / CPU second)\n");
199 	printf("%f\n", throughput);
200 }
201