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