4 #include <darwintest.h>
10 #include <sys/sysctl.h>
13 T_META_NAMESPACE("xnu.vm.perf"),
14 T_META_CHECK_LEAKS(false)
24 void allocate_zero_pages(char **buf
, int num_pages
, int vmpgsize
);
25 void allocate_mostly_zero_pages(char **buf
, int num_pages
, int vmpgsize
);
26 void allocate_random_pages(char **buf
, int num_pages
, int vmpgsize
);
27 void allocate_representative_pages(char **buf
, int num_pages
, int vmpgsize
);
28 void allocate_pages(int size_mb
, int page_type
);
29 void run_compressor_test(int size_mb
, int page_type
);
31 void allocate_zero_pages(char **buf
, int num_pages
, int vmpgsize
) {
34 for (i
= 0; i
< num_pages
; i
++) {
35 buf
[i
] = (char*)malloc((size_t)vmpgsize
* sizeof(char));
36 memset(buf
[i
], 0, vmpgsize
);
40 void allocate_mostly_zero_pages(char **buf
, int num_pages
, int vmpgsize
) {
43 for (i
= 0; i
< num_pages
; i
++) {
44 buf
[i
] = (char*)malloc((size_t)vmpgsize
* sizeof(char));
45 memset(buf
[i
], 0, vmpgsize
);
46 for (j
= 0; j
< 40; j
++) {
47 buf
[i
][j
] = (char)(j
+1);
52 void allocate_random_pages(char **buf
, int num_pages
, int vmpgsize
) {
55 for (i
= 0; i
< num_pages
; i
++) {
56 buf
[i
] = (char*)malloc((size_t)vmpgsize
* sizeof(char));
57 arc4random_buf((void*)buf
[i
], (size_t)vmpgsize
);
61 // Gives us the compression ratio we see in the typical case (~2.7)
62 void allocate_representative_pages(char **buf
, int num_pages
, int vmpgsize
) {
66 for (j
= 0; j
< num_pages
; j
++) {
67 buf
[j
] = (char*)malloc((size_t)vmpgsize
* sizeof(char));
69 for (i
= 0; i
< vmpgsize
; i
+= 16) {
70 memset(&buf
[j
][i
], val
, 16);
71 if (i
< 3700 * (vmpgsize
/ 4096)) {
78 void allocate_pages(int size_mb
, int page_type
) {
82 size_t vmpgsize_length
;
84 vmpgsize_length
= sizeof(vmpgsize
);
85 T_QUIET
; T_ASSERT_POSIX_SUCCESS(sysctlbyname("vm.pagesize", &vmpgsize
, &vmpgsize_length
, NULL
, 0),
86 "failed to query vm.pagesize");
88 T_FAIL("vm.pagesize set to zero");
91 num_pages
= size_mb
* 1024 * 1024 / vmpgsize
;
92 buf
= (char**)malloc(sizeof(char*) * (size_t)num_pages
);
94 // Switch on the type of page requested
97 allocate_zero_pages(buf
, num_pages
, vmpgsize
);
100 allocate_mostly_zero_pages(buf
, num_pages
, vmpgsize
);
103 allocate_random_pages(buf
, num_pages
, vmpgsize
);
106 allocate_representative_pages(buf
, num_pages
, vmpgsize
);
109 T_FAIL("unknown page type");
113 for(j
= 0; j
< num_pages
; j
++) {
119 void run_compressor_test(int size_mb
, int page_type
) {
121 #ifndef CONFIG_FREEZE
122 T_SKIP("Task freeze not supported.");
125 dt_stat_t r
= dt_stat_create("(input bytes / compressed bytes)", "compression_ratio");
126 dt_stat_time_t s
= dt_stat_time_create("compressor_latency");
128 while (!dt_stat_stable(s
)) {
130 int parent_pipe
[2], child_pipe
[2];
132 T_QUIET
; T_ASSERT_POSIX_SUCCESS(pipe(parent_pipe
), "pipe failed");
133 T_QUIET
; T_ASSERT_POSIX_SUCCESS(pipe(child_pipe
), "pipe failed");
136 T_QUIET
; T_ASSERT_POSIX_SUCCESS(pid
, "fork failed with %d", errno
);
141 close(child_pipe
[0]);
142 close(parent_pipe
[1]);
143 allocate_pages(size_mb
, page_type
);
145 // Indicates to the parent that the child has finished allocating pages
146 write(child_pipe
[1], &val
, sizeof(val
));
148 // Parent is done with the freeze, ok to exit now
149 read(parent_pipe
[0], &val
, sizeof(val
));
151 T_FAIL("pipe read error");
153 close(child_pipe
[1]);
154 close(parent_pipe
[0]);
159 int64_t compressed_before
, compressed_after
, input_before
, input_after
;
160 dt_stat_token start_token
;
161 size_t length
= sizeof(compressed_before
);
163 close(child_pipe
[1]);
164 close(parent_pipe
[0]);
166 // Wait for the child to finish allocating pages
167 read(child_pipe
[0], &val
, sizeof(val
));
169 T_FAIL("pipe read error");
171 // Just to be extra sure that the child has finished allocating all of its pages
174 T_LOG("attempting to freeze pid %d\n", pid
);
176 T_QUIET
; T_ASSERT_POSIX_SUCCESS(sysctlbyname("vm.compressor_compressed_bytes", &compressed_before
, &length
, NULL
, 0),
177 "failed to query vm.compressor_compressed_bytes");
178 T_QUIET
; T_ASSERT_POSIX_SUCCESS(sysctlbyname("vm.compressor_input_bytes", &input_before
, &length
, NULL
, 0),
179 "failed to query vm.compressor_input_bytes");
181 start_token
= dt_stat_time_begin(s
);
182 ret
= sysctlbyname("kern.memorystatus_freeze", NULL
, NULL
, &pid
, (size_t)sizeof(int));
183 dt_stat_time_end(s
, start_token
);
185 T_QUIET
; T_ASSERT_POSIX_SUCCESS(sysctlbyname("vm.compressor_compressed_bytes", &compressed_after
, &length
, NULL
, 0),
186 "failed to query vm.compressor_compressed_bytes");
187 T_QUIET
; T_ASSERT_POSIX_SUCCESS(sysctlbyname("vm.compressor_input_bytes", &input_after
, &length
, NULL
, 0),
188 "failed to query vm.compressor_input_bytes");
190 T_QUIET
; T_ASSERT_POSIX_SUCCESS(ret
, "sysctl kern.memorystatus_freeze failed on pid %d", pid
);
192 dt_stat_add(r
, (double)(input_after
- input_before
)/(double)(compressed_after
- compressed_before
));
195 // Ok for the child to exit now
196 write(parent_pipe
[1], &val
, sizeof(val
));
199 close(child_pipe
[0]);
200 close(parent_pipe
[1]);
208 // Numbers for 10MB and above are fairly reproducible. Anything smaller shows a lot of variation.
209 T_DECL(compr_10MB_zero
, "Compressor latencies") {
210 run_compressor_test(10, ALL_ZEROS
);
213 T_DECL(compr_10MB_mostly_zero
, "Compressor latencies") {
214 run_compressor_test(10, MOSTLY_ZEROS
);
217 T_DECL(compr_10MB_random
, "Compressor latencies") {
218 run_compressor_test(10, RANDOM
);
221 T_DECL(compr_10MB_typical
, "Compressor latencies") {
222 run_compressor_test(10, TYPICAL
);
225 T_DECL(compr_100MB_zero
, "Compressor latencies") {
226 run_compressor_test(100, ALL_ZEROS
);
229 T_DECL(compr_100MB_mostly_zero
, "Compressor latencies") {
230 run_compressor_test(100, MOSTLY_ZEROS
);
233 T_DECL(compr_100MB_random
, "Compressor latencies") {
234 run_compressor_test(100, RANDOM
);
237 T_DECL(compr_100MB_typical
, "Compressor latencies") {
238 run_compressor_test(100, TYPICAL
);