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 fd
= open("/dev/random", O_RDONLY
);
56 T_QUIET
; T_ASSERT_POSIX_SUCCESS(fd
, "open /dev/random failed [%s]\n", strerror(errno
));
58 for (i
= 0; i
< num_pages
; i
++) {
59 buf
[i
] = (char*)malloc((size_t)vmpgsize
* sizeof(char));
60 T_QUIET
; T_ASSERT_POSIX_SUCCESS(read(fd
, buf
[i
], (size_t)vmpgsize
),
61 "read from /dev/random failed [%s]\n", strerror(errno
));
66 // Gives us the compression ratio we see in the typical case (~2.7)
67 void allocate_representative_pages(char **buf
, int num_pages
, int vmpgsize
) {
71 for (j
= 0; j
< num_pages
; j
++) {
72 buf
[j
] = (char*)malloc((size_t)vmpgsize
* sizeof(char));
74 for (i
= 0; i
< vmpgsize
; i
+= 16) {
75 memset(&buf
[j
][i
], val
, 16);
76 if (i
< 3700 * (vmpgsize
/ 4096)) {
83 void allocate_pages(int size_mb
, int page_type
) {
87 size_t vmpgsize_length
;
89 vmpgsize_length
= sizeof(vmpgsize
);
90 T_QUIET
; T_ASSERT_POSIX_SUCCESS(sysctlbyname("vm.pagesize", &vmpgsize
, &vmpgsize_length
, NULL
, 0),
91 "failed to query vm.pagesize");
93 T_FAIL("vm.pagesize set to zero");
96 num_pages
= size_mb
* 1024 * 1024 / vmpgsize
;
97 buf
= (char**)malloc(sizeof(char*) * (size_t)num_pages
);
99 // Switch on the type of page requested
102 allocate_zero_pages(buf
, num_pages
, vmpgsize
);
105 allocate_mostly_zero_pages(buf
, num_pages
, vmpgsize
);
108 allocate_random_pages(buf
, num_pages
, vmpgsize
);
111 allocate_representative_pages(buf
, num_pages
, vmpgsize
);
114 T_FAIL("unknown page type");
118 for(j
= 0; j
< num_pages
; j
++) {
124 void run_compressor_test(int size_mb
, int page_type
) {
126 #ifndef CONFIG_FREEZE
127 T_SKIP("Task freeze not supported.");
130 dt_stat_t r
= dt_stat_create("(input bytes / compressed bytes)", "compression_ratio");
131 dt_stat_time_t s
= dt_stat_time_create("compressor_latency");
133 while (!dt_stat_stable(s
)) {
135 int parent_pipe
[2], child_pipe
[2];
137 T_QUIET
; T_ASSERT_POSIX_SUCCESS(pipe(parent_pipe
), "pipe failed");
138 T_QUIET
; T_ASSERT_POSIX_SUCCESS(pipe(child_pipe
), "pipe failed");
141 T_QUIET
; T_ASSERT_POSIX_SUCCESS(pid
, "fork failed with %d", errno
);
146 close(child_pipe
[0]);
147 close(parent_pipe
[1]);
148 allocate_pages(size_mb
, page_type
);
150 // Indicates to the parent that the child has finished allocating pages
151 write(child_pipe
[1], &val
, sizeof(val
));
153 // Parent is done with the freeze, ok to exit now
154 read(parent_pipe
[0], &val
, sizeof(val
));
156 T_FAIL("pipe read error");
158 close(child_pipe
[1]);
159 close(parent_pipe
[0]);
164 int64_t compressed_before
, compressed_after
, input_before
, input_after
;
165 dt_stat_token start_token
;
166 size_t length
= sizeof(compressed_before
);
168 close(child_pipe
[1]);
169 close(parent_pipe
[0]);
171 // Wait for the child to finish allocating pages
172 read(child_pipe
[0], &val
, sizeof(val
));
174 T_FAIL("pipe read error");
176 // Just to be extra sure that the child has finished allocating all of its pages
179 T_LOG("attempting to freeze pid %d\n", pid
);
181 T_QUIET
; T_ASSERT_POSIX_SUCCESS(sysctlbyname("vm.compressor_compressed_bytes", &compressed_before
, &length
, NULL
, 0),
182 "failed to query vm.compressor_compressed_bytes");
183 T_QUIET
; T_ASSERT_POSIX_SUCCESS(sysctlbyname("vm.compressor_input_bytes", &input_before
, &length
, NULL
, 0),
184 "failed to query vm.compressor_input_bytes");
186 start_token
= dt_stat_time_begin(s
);
187 ret
= sysctlbyname("kern.memorystatus_freeze", NULL
, NULL
, &pid
, (size_t)sizeof(int));
188 dt_stat_time_end(s
, start_token
);
190 T_QUIET
; T_ASSERT_POSIX_SUCCESS(sysctlbyname("vm.compressor_compressed_bytes", &compressed_after
, &length
, NULL
, 0),
191 "failed to query vm.compressor_compressed_bytes");
192 T_QUIET
; T_ASSERT_POSIX_SUCCESS(sysctlbyname("vm.compressor_input_bytes", &input_after
, &length
, NULL
, 0),
193 "failed to query vm.compressor_input_bytes");
195 T_QUIET
; T_ASSERT_POSIX_SUCCESS(ret
, "sysctl kern.memorystatus_freeze failed on pid %d", pid
);
197 dt_stat_add(r
, (double)(input_after
- input_before
)/(double)(compressed_after
- compressed_before
));
200 // Ok for the child to exit now
201 write(parent_pipe
[1], &val
, sizeof(val
));
204 close(child_pipe
[0]);
205 close(parent_pipe
[1]);
213 // Numbers for 10MB and above are fairly reproducible. Anything smaller shows a lot of variation.
214 T_DECL(compr_10MB_zero
, "Compressor latencies") {
215 run_compressor_test(10, ALL_ZEROS
);
218 T_DECL(compr_10MB_mostly_zero
, "Compressor latencies") {
219 run_compressor_test(10, MOSTLY_ZEROS
);
222 T_DECL(compr_10MB_random
, "Compressor latencies") {
223 run_compressor_test(10, RANDOM
);
226 T_DECL(compr_10MB_typical
, "Compressor latencies") {
227 run_compressor_test(10, TYPICAL
);
230 T_DECL(compr_100MB_zero
, "Compressor latencies") {
231 run_compressor_test(100, ALL_ZEROS
);
234 T_DECL(compr_100MB_mostly_zero
, "Compressor latencies") {
235 run_compressor_test(100, MOSTLY_ZEROS
);
238 T_DECL(compr_100MB_random
, "Compressor latencies") {
239 run_compressor_test(100, RANDOM
);
242 T_DECL(compr_100MB_typical
, "Compressor latencies") {
243 run_compressor_test(100, TYPICAL
);