]> git.saurik.com Git - apple/xnu.git/blob - tests/vm/perf_madvise.c
xnu-7195.101.1.tar.gz
[apple/xnu.git] / tests / vm / perf_madvise.c
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
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
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 *
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
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
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
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
195 output_throughput(double throughput)
196 {
197 printf("-----Results-----\n");
198 printf("Throughput (bytes / CPU second)\n");
199 printf("%f\n", throughput);
200 }