1 // Copyright 2016 The Fuchsia Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include <stdio.h>
6 #include <limits.h>
7 #include <inttypes.h>
8 #include <sys/types.h>
9 #include <stdlib.h>
10 #include <unistd.h>
11
12 #include <zircon/compiler.h>
13 #include <zircon/process.h>
14 #include <zircon/syscalls.h>
15 #include <fbl/algorithm.h>
16
17 #include "bench.h"
18
ns_to_ticks(zx_time_t ns)19 static zx_ticks_t ns_to_ticks(zx_time_t ns) {
20 __uint128_t temp = (__uint128_t)ns * zx_ticks_per_second() / ZX_SEC(1);
21 return (zx_ticks_t)temp;
22 }
23
ticks_to_ns(zx_ticks_t ticks)24 static zx_time_t ticks_to_ns(zx_ticks_t ticks) {
25 __uint128_t temp = (__uint128_t)ticks * ZX_SEC(1) / zx_ticks_per_second();
26 return (zx_time_t)temp;
27 }
28
29 // spin the cpu a bit to make sure the frequency is cranked to the top
spin(zx_time_t nanosecs)30 static void spin(zx_time_t nanosecs) {
31 zx_ticks_t target_ticks = ns_to_ticks(nanosecs);
32 zx_ticks_t t = zx_ticks_get();
33
34 while (zx_ticks_get() - t < target_ticks)
35 ;
36 }
37
38 template <typename T>
time_it(T func)39 inline zx_time_t time_it(T func) {
40 spin(ZX_MSEC(10));
41
42 zx_ticks_t ticks = zx_ticks_get();
43 func();
44 ticks = zx_ticks_get() - ticks;
45
46 return ticks_to_ns(ticks);
47 }
48
vmo_run_benchmark()49 int vmo_run_benchmark() {
50 zx_time_t t;
51 //zx_handle_t vmo;
52
53 printf("starting VMO benchmark\n");
54
55 // allocate a bunch of large vmos, delete them
56 const size_t size = 32*1024*1024;
57 zx_handle_t vmos[32];
58 uintptr_t ptr;
59
60 t = time_it([&](){
61 for (auto& vmo : vmos) {
62 zx_vmo_create(size, 0, &vmo);
63 }
64 });
65
66 printf("\ttook %" PRIu64 " nsecs to create %zu vmos of size %zu\n", t, fbl::count_of(vmos), size);
67
68 t = time_it([&](){
69 for (auto& vmo : vmos) {
70 zx_handle_close(vmo);
71 }
72 });
73 printf("\ttook %" PRIu64 " nsecs to delete %zu vmos of size %zu\n", t, fbl::count_of(vmos), size);
74
75 // create a vmo and demand fault it in
76 zx_handle_t vmo;
77 zx_vmo_create(size, 0, &vmo);
78
79 zx_vmar_map(zx_vmar_root_self(), ZX_VM_PERM_READ | ZX_VM_PERM_WRITE, 0, vmo, 0, size, &ptr);
80
81 t = time_it([&](){
82 for (size_t i = 0; i < size; i += PAGE_SIZE) {
83 __UNUSED char a = ((volatile char *)ptr)[i];
84 }
85 });
86 printf("\ttook %" PRIu64 " nsecs to read fault in vmo of size %zu (should be read faulting a zero page)\n", t, size);
87
88 t = time_it([&](){
89 for (size_t i = 0; i < size; i += PAGE_SIZE) {
90 __UNUSED char a = ((volatile char *)ptr)[i];
91 }
92 });
93 printf("\ttook %" PRIu64 " nsecs to read in vmo of size %zu a second time (should be mapped already)\n", t, size);
94
95 t = time_it([&](){
96 for (size_t i = 0; i < size; i += PAGE_SIZE) {
97 ((volatile char *)ptr)[i] = 99;
98 }
99 });
100 printf("\ttook %" PRIu64 " nsecs to write fault in vmo of size %zu after read faulting it\n", t, size);
101
102 t = time_it([&](){
103 for (size_t i = 0; i < size; i += PAGE_SIZE) {
104 ((volatile char *)ptr)[i] = 99;
105 }
106 });
107 printf("\ttook %" PRIu64 " nsecs to write fault in vmo of size %zu a second time\n", t, size);
108
109 // unmap the original mapping
110 t = time_it([&](){
111 zx_vmar_unmap(zx_vmar_root_self(), ptr, size);
112 });
113 printf("\ttook %" PRIu64 " nsecs to unmap the vmo %zu (%zu pages)\n", t, size, size / PAGE_SIZE);
114
115 // map it a again and time read faulting it
116 zx_vmar_map(zx_vmar_root_self(), ZX_VM_PERM_READ | ZX_VM_PERM_WRITE, 0, vmo, 0, size, &ptr);
117
118 t = time_it([&](){
119 for (size_t i = 0; i < size; i += PAGE_SIZE) {
120 __UNUSED char a = ((volatile char *)ptr)[i];
121 }
122 });
123 printf("\ttook %" PRIu64 " nsecs to read fault in vmo of size %zu in another mapping\n", t, size);
124
125 zx_vmar_unmap(zx_vmar_root_self(), ptr, size);
126
127 // map it a again and time write faulting it
128 zx_vmar_map(zx_vmar_root_self(), ZX_VM_PERM_READ | ZX_VM_PERM_WRITE, 0, vmo, 0, size, &ptr);
129
130 t = time_it([&](){
131 for (size_t i = 0; i < size; i += PAGE_SIZE) {
132 ((volatile char *)ptr)[i] = 99;
133 }
134 });
135 printf("\ttook %" PRIu64 " nsecs to write fault in vmo of size %zu in another mapping\n", t, size);
136
137 zx_vmar_unmap(zx_vmar_root_self(), ptr, size);
138
139 // delete the vmo
140 t = time_it([&](){
141 zx_handle_close(vmo);
142 });
143 printf("\ttook %" PRIu64 " nsecs to delete populated vmo of size %zu\n", t, size);
144
145 // create a second vmo and write fault it in directly
146 zx_vmo_create(size, 0, &vmo);
147
148 zx_vmar_map(zx_vmar_root_self(), ZX_VM_PERM_READ | ZX_VM_PERM_WRITE, 0, vmo, 0, size, &ptr);
149
150 t = time_it([&](){
151 for (size_t i = 0; i < size; i += PAGE_SIZE) {
152 ((volatile char *)ptr)[i] = 99;
153 }
154 });
155 printf("\ttook %" PRIu64 " nsecs to write fault in vmo of size %zu\n", t, size);
156
157 zx_handle_close(vmo);
158
159 // create a vmo and commit and decommit it directly
160 zx_vmo_create(size, 0, &vmo);
161
162 t = time_it([&](){
163 zx_vmo_op_range(vmo, ZX_VMO_OP_COMMIT, 0, size, nullptr, 0);
164 });
165 printf("\ttook %" PRIu64 " nsecs to commit vmo of size %zu\n", t, size);
166
167 t = time_it([&](){
168 zx_status_t status = zx_vmo_op_range(vmo, ZX_VMO_OP_COMMIT, 0, size, nullptr, 0);
169 if (status != ZX_OK) {
170 __builtin_trap();
171 }
172 });
173 printf("\ttook %" PRIu64 " nsecs to commit already committed vmo of size %zu\n", t, size);
174
175 t = time_it([&](){
176 zx_vmo_op_range(vmo, ZX_VMO_OP_DECOMMIT, 0, size, nullptr, 0);
177 });
178 printf("\ttook %" PRIu64 " nsecs to decommit vmo of size %zu\n", t, size);
179
180 zx_handle_close(vmo);
181
182 printf("done with benchmark\n");
183
184 return 0;
185 }
186