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
19static 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
24static 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
30static 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
38template <typename T>
39inline 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
49int 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