blob: 34ffa2ea74fbb75b176d4b8170aa7d132b636308 [file] [log] [blame]
Suren Baghdasaryan9a0a3602023-08-17 21:52:50 +00001/*
2 * Copyright (C) 2023 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include <string.h>
18#include <sys/mman.h>
19#include <sys/syscall.h>
20
21#include <android-base/file.h>
22#include <android-base/stringprintf.h>
23#include <benchmark/benchmark.h>
24
25#include "util.h"
26
Christopher Ferris3e281b12023-12-13 14:30:56 -080027enum BenchmarkType : uint8_t {
28 kBenchmarkMmapOnly,
29 kBenchmarkMunmapOnly,
30 kBenchmarkAll,
31};
32
Suren Baghdasaryan9a0a3602023-08-17 21:52:50 +000033static size_t page_sz = getpagesize();
34
35struct MmapParams {
36 int prot;
37 int flags;
38 int64_t size;
39};
40
Christopher Ferris3e281b12023-12-13 14:30:56 -080041template <BenchmarkType type>
42void MmapBenchmarkImpl(benchmark::State& state, const struct MmapParams& params, int fd,
43 void* area = nullptr) {
Suren Baghdasaryan9a0a3602023-08-17 21:52:50 +000044 for (auto _ : state) {
Christopher Ferris3e281b12023-12-13 14:30:56 -080045 if (type == kBenchmarkMunmapOnly) state.PauseTiming();
Suren Baghdasaryan9a0a3602023-08-17 21:52:50 +000046 void* addr = mmap(area, params.size, params.prot, params.flags, fd, 0);
47 if (addr == MAP_FAILED) {
48 state.SkipWithError(android::base::StringPrintf("mmap failed: %s", strerror(errno)).c_str());
49 break;
50 }
51
Christopher Ferris3e281b12023-12-13 14:30:56 -080052 if (type == kBenchmarkMmapOnly) state.PauseTiming();
53
Suren Baghdasaryan9a0a3602023-08-17 21:52:50 +000054 if (params.prot & PROT_WRITE) {
55 MakeAllocationResident(addr, params.size, page_sz);
56 }
57
Christopher Ferris3e281b12023-12-13 14:30:56 -080058 if (type == kBenchmarkMunmapOnly) state.ResumeTiming();
59
Suren Baghdasaryan9a0a3602023-08-17 21:52:50 +000060 if (munmap(addr, params.size) != 0) {
61 state.SkipWithError(
62 android::base::StringPrintf("munmap failed: %s", strerror(errno)).c_str());
63 break;
64 }
Christopher Ferris3e281b12023-12-13 14:30:56 -080065 if (type == kBenchmarkMmapOnly) state.ResumeTiming();
Suren Baghdasaryan9a0a3602023-08-17 21:52:50 +000066 }
67}
68
Christopher Ferris3e281b12023-12-13 14:30:56 -080069static void MmapBenchmark(benchmark::State& state, const struct MmapParams& params, int fd,
70 void* area = nullptr) {
71 MmapBenchmarkImpl<kBenchmarkAll>(state, params, fd, area);
72}
73
Suren Baghdasaryan9a0a3602023-08-17 21:52:50 +000074static void MmapFixedBenchmark(benchmark::State& state, const struct MmapParams& params, int fd,
75 size_t area_size, size_t offs) {
Christopher Ferris3e281b12023-12-13 14:30:56 -080076 if ((params.flags & MAP_FIXED) == 0) {
77 state.SkipWithError("MmapFixedBenchmark called without MAP_FIXED set");
78 return;
79 }
80
81 // Create the mmap that will be used for the fixed mmaps.
82 uint8_t* area = reinterpret_cast<uint8_t*>(
83 mmap(nullptr, area_size, params.prot, params.flags & ~MAP_FIXED, fd, 0));
Suren Baghdasaryan9a0a3602023-08-17 21:52:50 +000084 if (area == MAP_FAILED) {
85 state.SkipWithError(android::base::StringPrintf("mmap failed: %s", strerror(errno)).c_str());
86 return;
87 }
88
89 MmapBenchmark(state, params, fd, area + offs);
90
91 if (munmap(area, area_size) != 0) {
92 state.SkipWithError(android::base::StringPrintf("munmap failed: %s", strerror(errno)).c_str());
93 return;
94 }
95}
96
97static void MmapFileBenchmark(benchmark::State& state, const struct MmapParams& params,
98 size_t area_size, size_t offs) {
99 TemporaryFile tf;
100
101 if (tf.fd < 0) {
102 state.SkipWithError(
103 android::base::StringPrintf("failed to create a temporary file: %s", strerror(errno))
104 .c_str());
105 return;
106 }
107
108 if (area_size > 0 && ftruncate(tf.fd, area_size)) {
109 state.SkipWithError(
110 android::base::StringPrintf("ftruncate failed: %s", strerror(errno)).c_str());
111 return;
112 }
113
114 if (params.flags & MAP_FIXED) {
115 MmapFixedBenchmark(state, params, tf.fd, area_size, offs);
116 } else {
117 MmapBenchmark(state, params, tf.fd);
118 }
119}
120
121// anon mmap
122static void BM_syscall_mmap_anon_rw(benchmark::State& state) {
123 struct MmapParams params = {
124 .prot = PROT_READ | PROT_WRITE,
125 .flags = MAP_PRIVATE | MAP_ANONYMOUS,
126 .size = state.range(0),
127 };
128
129 MmapBenchmark(state, params, 0);
130}
131BIONIC_BENCHMARK_WITH_ARG(BM_syscall_mmap_anon_rw, "AT_All_PAGE_SIZES");
132
133static void BM_syscall_mmap_anon_noreserve(benchmark::State& state) {
134 struct MmapParams params = {
135 .prot = PROT_NONE,
136 .flags = MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE,
137 .size = state.range(0),
138 };
139
140 MmapBenchmark(state, params, 0);
141}
142BIONIC_BENCHMARK_WITH_ARG(BM_syscall_mmap_anon_noreserve, "AT_All_PAGE_SIZES");
143
144static void BM_syscall_mmap_anon_none(benchmark::State& state) {
145 struct MmapParams params = {
146 .prot = PROT_NONE,
147 .flags = MAP_PRIVATE | MAP_ANONYMOUS,
148 .size = state.range(0),
149 };
150
151 MmapBenchmark(state, params, 0);
152}
153BIONIC_BENCHMARK_WITH_ARG(BM_syscall_mmap_anon_none, "AT_All_PAGE_SIZES");
154
155// anon fixed mmap
156static void BM_syscall_mmap_anon_rw_fixed(benchmark::State& state) {
157 struct MmapParams params = {
158 .prot = PROT_READ | PROT_WRITE,
159 .flags = MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED,
160 .size = state.range(0),
161 };
162
Christopher Ferris3e281b12023-12-13 14:30:56 -0800163 MmapFixedBenchmark(state, params, -1, params.size, 0);
Suren Baghdasaryan9a0a3602023-08-17 21:52:50 +0000164}
165BIONIC_BENCHMARK_WITH_ARG(BM_syscall_mmap_anon_rw_fixed, "AT_All_PAGE_SIZES");
166
167static void BM_syscall_mmap_anon_none_fixed(benchmark::State& state) {
168 struct MmapParams params = {
169 .prot = PROT_NONE,
170 .flags = MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED,
171 .size = state.range(0),
172 };
173
Christopher Ferris3e281b12023-12-13 14:30:56 -0800174 MmapFixedBenchmark(state, params, -1, params.size, 0);
Suren Baghdasaryan9a0a3602023-08-17 21:52:50 +0000175}
176BIONIC_BENCHMARK_WITH_ARG(BM_syscall_mmap_anon_none_fixed, "AT_All_PAGE_SIZES");
177
178// file mmap
179static void BM_syscall_mmap_file_rd_priv(benchmark::State& state) {
180 struct MmapParams params = {
181 .prot = PROT_READ,
182 .flags = MAP_PRIVATE,
183 .size = state.range(0),
184 };
185
186 MmapFileBenchmark(state, params, params.size, 0);
187}
188BIONIC_BENCHMARK_WITH_ARG(BM_syscall_mmap_file_rd_priv, "AT_All_PAGE_SIZES");
189
190static void BM_syscall_mmap_file_rw_shared(benchmark::State& state) {
191 struct MmapParams params = {
192 .prot = PROT_READ | PROT_WRITE,
193 .flags = MAP_SHARED,
194 .size = state.range(0),
195 };
196
197 MmapFileBenchmark(state, params, params.size, 0);
198}
199BIONIC_BENCHMARK_WITH_ARG(BM_syscall_mmap_file_rw_shared, "AT_All_PAGE_SIZES");
200
201// file fixed mmap
202static void BM_syscall_mmap_file_rw_priv_fixed_start(benchmark::State& state) {
203 struct MmapParams params = {
204 .prot = PROT_READ | PROT_WRITE,
205 .flags = MAP_PRIVATE | MAP_FIXED,
206 .size = state.range(0),
207 };
208
209 // allocate 3x area and map at the start
210 MmapFileBenchmark(state, params, params.size * 3, 0);
211}
212BIONIC_BENCHMARK_WITH_ARG(BM_syscall_mmap_file_rw_priv_fixed_start, "AT_All_PAGE_SIZES");
213
214static void BM_syscall_mmap_file_rw_priv_fixed_mid(benchmark::State& state) {
215 struct MmapParams params = {
216 .prot = PROT_READ | PROT_WRITE,
217 .flags = MAP_PRIVATE | MAP_FIXED,
218 .size = state.range(0),
219 };
220
221 // allocate 3x area and map at the middle
222 MmapFileBenchmark(state, params, params.size * 3, params.size);
223}
224// mapping at sub-page size offset is not supported, so run only for AT_MULTI_PAGE_SIZES
225BIONIC_BENCHMARK_WITH_ARG(BM_syscall_mmap_file_rw_priv_fixed_mid, "AT_MULTI_PAGE_SIZES");
226
227static void BM_syscall_mmap_file_rw_priv_fixed_end(benchmark::State& state) {
228 struct MmapParams params = {
229 .prot = PROT_READ | PROT_WRITE,
230 .flags = MAP_PRIVATE | MAP_FIXED,
231 .size = state.range(0),
232 };
233
234 // allocate 3x area and map at the end
235 MmapFileBenchmark(state, params, params.size * 3, params.size * 2);
236}
237// mapping at sub-page size offset is not supported, so run only for AT_MULTI_PAGE_SIZES
238BIONIC_BENCHMARK_WITH_ARG(BM_syscall_mmap_file_rw_priv_fixed_end, "AT_MULTI_PAGE_SIZES");
Christopher Ferris3e281b12023-12-13 14:30:56 -0800239
240static void BM_syscall_mmap_anon_mmap_only(benchmark::State& state) {
241 struct MmapParams params = {
242 .prot = PROT_READ | PROT_WRITE,
243 .flags = MAP_PRIVATE | MAP_ANONYMOUS,
244 .size = state.range(0),
245 };
246 MmapBenchmarkImpl<kBenchmarkMmapOnly>(state, params, 0);
247}
248BIONIC_BENCHMARK_WITH_ARG(BM_syscall_mmap_anon_mmap_only, "AT_MULTI_PAGE_SIZES");
249
250static void BM_syscall_mmap_anon_munmap_only(benchmark::State& state) {
251 struct MmapParams params = {
252 .prot = PROT_READ | PROT_WRITE,
253 .flags = MAP_PRIVATE | MAP_ANONYMOUS,
254 .size = state.range(0),
255 };
256 MmapBenchmarkImpl<kBenchmarkMunmapOnly>(state, params, 0);
257}
258BIONIC_BENCHMARK_WITH_ARG(BM_syscall_mmap_anon_munmap_only, "AT_MULTI_PAGE_SIZES");
259
260void MadviseBenchmark(benchmark::State& state, const struct MmapParams& params, int madvise_flags) {
261 void* addr = mmap(nullptr, params.size, params.prot, params.flags, 0, 0);
262 if (addr == MAP_FAILED) {
263 state.SkipWithError(android::base::StringPrintf("mmap failed: %s", strerror(errno)).c_str());
264 return;
265 }
266 for (auto _ : state) {
267 state.PauseTiming();
268 if (params.prot & PROT_WRITE) {
269 MakeAllocationResident(addr, params.size, page_sz);
270 }
271 state.ResumeTiming();
272
273 madvise(addr, params.size, madvise_flags);
274 }
275
276 if (munmap(addr, params.size) != 0) {
277 state.SkipWithError(android::base::StringPrintf("munmap failed: %s", strerror(errno)).c_str());
278 }
279}
280
281static void BM_syscall_mmap_anon_madvise_dontneed(benchmark::State& state) {
282 struct MmapParams params = {
283 .prot = PROT_READ | PROT_WRITE,
284 .flags = MAP_PRIVATE | MAP_ANONYMOUS,
285 .size = state.range(0),
286 };
287 MadviseBenchmark(state, params, MADV_DONTNEED);
288}
289BIONIC_BENCHMARK_WITH_ARG(BM_syscall_mmap_anon_madvise_dontneed, "AT_MULTI_PAGE_SIZES");
290
291static void BM_syscall_mmap_anon_madvise_pageout(benchmark::State& state) {
292 struct MmapParams params = {
293 .prot = PROT_READ | PROT_WRITE,
294 .flags = MAP_PRIVATE | MAP_ANONYMOUS,
295 .size = state.range(0),
296 };
297 MadviseBenchmark(state, params, MADV_PAGEOUT);
298}
299BIONIC_BENCHMARK_WITH_ARG(BM_syscall_mmap_anon_madvise_pageout, "AT_MULTI_PAGE_SIZES");
300
301static void BM_syscall_mmap_anon_madvise_free(benchmark::State& state) {
302 struct MmapParams params = {
303 .prot = PROT_READ | PROT_WRITE,
304 .flags = MAP_PRIVATE | MAP_ANONYMOUS,
305 .size = state.range(0),
306 };
307 MadviseBenchmark(state, params, MADV_FREE);
308}
309BIONIC_BENCHMARK_WITH_ARG(BM_syscall_mmap_anon_madvise_free, "AT_MULTI_PAGE_SIZES");