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