blob: f9ceb4c2997d05374b3fafbb06ad84aa3010c053 [file] [log] [blame]
Elliott Hughesb28e4902014-03-11 11:19:06 -07001/*
2 * Copyright (C) 2014 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
Anders Lewisa98a5fb2017-08-09 16:52:19 -070017#include <err.h>
Elliott Hughes3b644e92017-12-11 14:43:52 -080018#include <inttypes.h>
Elliott Hughesb28e4902014-03-11 11:19:06 -070019#include <stdio.h>
Elliott Hughes8c4994b2015-01-20 18:09:05 -080020#include <stdio_ext.h>
Elliott Hughesc2173732015-05-13 13:18:04 -070021#include <stdlib.h>
Elliott Hughesb28e4902014-03-11 11:19:06 -070022
Mark Salyzynba1a7232018-11-14 15:19:53 -080023#include <android-base/file.h>
Elliott Hughes281e06b2016-02-17 10:23:52 -080024#include <benchmark/benchmark.h>
Anders Lewisa7b0f882017-07-24 20:01:13 -070025#include "util.h"
Elliott Hughesb28e4902014-03-11 11:19:06 -070026
Elliott Hughes938bece2017-08-23 09:52:50 -070027static void FillFile(TemporaryFile& tf) {
28 char line[256];
29 memset(line, 'x', sizeof(line));
30 line[sizeof(line) - 1] = '\0';
31
Elliott Hughes5cec3772018-01-19 15:45:23 -080032 FILE* fp = fopen(tf.path, "we");
Elliott Hughes938bece2017-08-23 09:52:50 -070033 for (size_t i = 0; i < 4096; ++i) fputs(line, fp);
34 fclose(fp);
35}
36
Elliott Hughes47dc7c92014-12-01 13:12:18 -080037template <typename Fn>
Elliott Hughes281e06b2016-02-17 10:23:52 -080038void ReadWriteTest(benchmark::State& state, Fn f, bool buffered) {
Martijn Coenenbe763d82016-11-14 14:16:08 +010039 size_t chunk_size = state.range(0);
Elliott Hughes281e06b2016-02-17 10:23:52 -080040
Elliott Hughesf957ac02025-01-14 14:11:53 -050041 // /dev/zero copies zeroes if you read from it and discards writes.
42 //
43 // This is fine for the purpose of measuring stdio overhead
44 // rather than kernel/fs performance.
45 // (Old versions of stdio would copy reads/writes larger than
46 // the stdio buffer through the stdio buffer in chunks,
47 // rather than directly to the user's destination.)
Elliott Hughesd706fe52017-08-22 15:26:07 -070048 FILE* fp = fopen("/dev/zero", "r+e");
Elliott Hughes8c4994b2015-01-20 18:09:05 -080049 __fsetlocking(fp, FSETLOCKING_BYCALLER);
Elliott Hughesb28e4902014-03-11 11:19:06 -070050 char* buf = new char[chunk_size];
Elliott Hughesb28e4902014-03-11 11:19:06 -070051
Elliott Hughes47dc7c92014-12-01 13:12:18 -080052 if (!buffered) {
Yi Kong32bc0fc2018-08-02 17:31:13 -070053 setvbuf(fp, nullptr, _IONBF, 0);
Elliott Hughes47dc7c92014-12-01 13:12:18 -080054 }
55
Elliott Hughes281e06b2016-02-17 10:23:52 -080056 while (state.KeepRunning()) {
Elliott Hughesd706fe52017-08-22 15:26:07 -070057 if (f(buf, chunk_size, 1, fp) != 1) {
58 errx(1, "ERROR: op of %zu bytes failed.", chunk_size);
59 }
Elliott Hughesb28e4902014-03-11 11:19:06 -070060 }
61
Elliott Hughes281e06b2016-02-17 10:23:52 -080062 state.SetBytesProcessed(int64_t(state.iterations()) * int64_t(chunk_size));
Elliott Hughesb28e4902014-03-11 11:19:06 -070063 delete[] buf;
64 fclose(fp);
65}
Elliott Hughes47dc7c92014-12-01 13:12:18 -080066
Elliott Hughes281e06b2016-02-17 10:23:52 -080067void BM_stdio_fread(benchmark::State& state) {
68 ReadWriteTest(state, fread, true);
Elliott Hughes47dc7c92014-12-01 13:12:18 -080069}
Christopher Ferris858e3362017-11-30 08:53:15 -080070BIONIC_BENCHMARK_WITH_ARG(BM_stdio_fread, "AT_COMMON_SIZES");
Elliott Hughesb28e4902014-03-11 11:19:06 -070071
Elliott Hughes281e06b2016-02-17 10:23:52 -080072void BM_stdio_fwrite(benchmark::State& state) {
73 ReadWriteTest(state, fwrite, true);
Elliott Hughesb28e4902014-03-11 11:19:06 -070074}
Christopher Ferris858e3362017-11-30 08:53:15 -080075BIONIC_BENCHMARK_WITH_ARG(BM_stdio_fwrite, "AT_COMMON_SIZES");
Elliott Hughes47dc7c92014-12-01 13:12:18 -080076
Elliott Hughes281e06b2016-02-17 10:23:52 -080077void BM_stdio_fread_unbuffered(benchmark::State& state) {
78 ReadWriteTest(state, fread, false);
Elliott Hughes47dc7c92014-12-01 13:12:18 -080079}
Christopher Ferris858e3362017-11-30 08:53:15 -080080BIONIC_BENCHMARK_WITH_ARG(BM_stdio_fread_unbuffered, "AT_COMMON_SIZES");
Elliott Hughes47dc7c92014-12-01 13:12:18 -080081
Elliott Hughes281e06b2016-02-17 10:23:52 -080082void BM_stdio_fwrite_unbuffered(benchmark::State& state) {
83 ReadWriteTest(state, fwrite, false);
Elliott Hughes47dc7c92014-12-01 13:12:18 -080084}
Christopher Ferris858e3362017-11-30 08:53:15 -080085BIONIC_BENCHMARK_WITH_ARG(BM_stdio_fwrite_unbuffered, "AT_COMMON_SIZES");
Elliott Hughes1cf32f82015-01-16 17:08:31 -080086
Elliott Hughes938bece2017-08-23 09:52:50 -070087#if !defined(__GLIBC__)
88static void FopenFgetlnFclose(benchmark::State& state, bool no_locking) {
89 TemporaryFile tf;
90 FillFile(tf);
Elliott Hughes281e06b2016-02-17 10:23:52 -080091 while (state.KeepRunning()) {
Elliott Hughes938bece2017-08-23 09:52:50 -070092 FILE* fp = fopen(tf.path, "re");
Elliott Hughes8c4994b2015-01-20 18:09:05 -080093 if (no_locking) __fsetlocking(fp, FSETLOCKING_BYCALLER);
Elliott Hughes938bece2017-08-23 09:52:50 -070094 size_t length;
95 while (fgetln(fp, &length) != nullptr) {
96 }
97 fclose(fp);
98 }
99}
100
101static void BM_stdio_fopen_fgetln_fclose_locking(benchmark::State& state) {
102 FopenFgetlnFclose(state, false);
103}
104BIONIC_BENCHMARK(BM_stdio_fopen_fgetln_fclose_locking);
105
106void BM_stdio_fopen_fgetln_fclose_no_locking(benchmark::State& state) {
107 FopenFgetlnFclose(state, true);
108}
109BIONIC_BENCHMARK(BM_stdio_fopen_fgetln_fclose_no_locking);
110#endif
111
112static void FopenFgetsFclose(benchmark::State& state, bool no_locking) {
113 TemporaryFile tf;
114 FillFile(tf);
115 char buf[BUFSIZ];
116 while (state.KeepRunning()) {
117 FILE* fp = fopen(tf.path, "re");
118 if (no_locking) __fsetlocking(fp, FSETLOCKING_BYCALLER);
119 while (fgets(buf, sizeof(buf), fp) != nullptr) {
Anders Lewisa98a5fb2017-08-09 16:52:19 -0700120 }
Elliott Hughes1cf32f82015-01-16 17:08:31 -0800121 fclose(fp);
122 }
123}
Elliott Hughes8c4994b2015-01-20 18:09:05 -0800124
Elliott Hughes281e06b2016-02-17 10:23:52 -0800125static void BM_stdio_fopen_fgets_fclose_locking(benchmark::State& state) {
126 FopenFgetsFclose(state, false);
Elliott Hughes8c4994b2015-01-20 18:09:05 -0800127}
Anders Lewisa7b0f882017-07-24 20:01:13 -0700128BIONIC_BENCHMARK(BM_stdio_fopen_fgets_fclose_locking);
Elliott Hughes8c4994b2015-01-20 18:09:05 -0800129
Elliott Hughes281e06b2016-02-17 10:23:52 -0800130void BM_stdio_fopen_fgets_fclose_no_locking(benchmark::State& state) {
131 FopenFgetsFclose(state, true);
Elliott Hughes8c4994b2015-01-20 18:09:05 -0800132}
Anders Lewisa7b0f882017-07-24 20:01:13 -0700133BIONIC_BENCHMARK(BM_stdio_fopen_fgets_fclose_no_locking);
Anders Lewisac4f4b42017-08-08 18:29:51 -0700134
Elliott Hughes938bece2017-08-23 09:52:50 -0700135static void FopenGetlineFclose(benchmark::State& state, bool no_locking) {
136 TemporaryFile tf;
137 FillFile(tf);
138 while (state.KeepRunning()) {
139 FILE* fp = fopen(tf.path, "re");
140 if (no_locking) __fsetlocking(fp, FSETLOCKING_BYCALLER);
141 char* line = nullptr;
142 size_t n = 0;
143 while (getline(&line, &n, fp) != -1) {
144 }
145 free(line);
146 fclose(fp);
147 }
148}
149
150static void BM_stdio_fopen_getline_fclose_locking(benchmark::State& state) {
151 FopenGetlineFclose(state, false);
152}
153BIONIC_BENCHMARK(BM_stdio_fopen_getline_fclose_locking);
154
155void BM_stdio_fopen_getline_fclose_no_locking(benchmark::State& state) {
156 FopenGetlineFclose(state, true);
157}
158BIONIC_BENCHMARK(BM_stdio_fopen_getline_fclose_no_locking);
159
Anders Lewisac4f4b42017-08-08 18:29:51 -0700160static void FopenFgetcFclose(benchmark::State& state, bool no_locking) {
161 size_t nbytes = state.range(0);
162 while (state.KeepRunning()) {
163 FILE* fp = fopen("/dev/zero", "re");
164 if (no_locking) __fsetlocking(fp, FSETLOCKING_BYCALLER);
Anders Lewisac4f4b42017-08-08 18:29:51 -0700165 for (size_t i = 0; i < nbytes; ++i) {
Elliott Hughese8693e72020-10-22 13:43:59 -0700166 benchmark::DoNotOptimize(fgetc(fp));
Anders Lewisac4f4b42017-08-08 18:29:51 -0700167 }
168 fclose(fp);
169 }
170}
171
172static void BM_stdio_fopen_fgetc_fclose_locking(benchmark::State& state) {
173 FopenFgetcFclose(state, false);
174}
Christopher Ferris858e3362017-11-30 08:53:15 -0800175BIONIC_BENCHMARK_WITH_ARG(BM_stdio_fopen_fgetc_fclose_locking, "1024");
Anders Lewisac4f4b42017-08-08 18:29:51 -0700176
177void BM_stdio_fopen_fgetc_fclose_no_locking(benchmark::State& state) {
178 FopenFgetcFclose(state, true);
179}
Christopher Ferris858e3362017-11-30 08:53:15 -0800180BIONIC_BENCHMARK_WITH_ARG(BM_stdio_fopen_fgetc_fclose_no_locking, "1024");
Elliott Hughes92805992017-10-26 15:41:49 -0700181
182static void BM_stdio_printf_literal(benchmark::State& state) {
183 while (state.KeepRunning()) {
184 char buf[BUFSIZ];
185 snprintf(buf, sizeof(buf), "this is just a literal string with no format specifiers");
186 }
187}
188BIONIC_BENCHMARK(BM_stdio_printf_literal);
189
190static void BM_stdio_printf_s(benchmark::State& state) {
191 while (state.KeepRunning()) {
192 char buf[BUFSIZ];
193 snprintf(buf, sizeof(buf), "this is a more typical error message with detail: %s",
194 "No such file or directory");
195 }
196}
197BIONIC_BENCHMARK(BM_stdio_printf_s);
198
199static void BM_stdio_printf_d(benchmark::State& state) {
200 while (state.KeepRunning()) {
201 char buf[BUFSIZ];
202 snprintf(buf, sizeof(buf), "this is a more typical error message with detail: %d", 123456);
203 }
204}
205BIONIC_BENCHMARK(BM_stdio_printf_d);
Elliott Hughes5305a4d2017-11-03 14:00:37 -0700206
207static void BM_stdio_printf_1$s(benchmark::State& state) {
208 while (state.KeepRunning()) {
209 char buf[BUFSIZ];
210 snprintf(buf, sizeof(buf), "this is a more typical error message with detail: %1$s",
211 "No such file or directory");
212 }
213}
214BIONIC_BENCHMARK(BM_stdio_printf_1$s);
Elliott Hughes3b644e92017-12-11 14:43:52 -0800215
216static void BM_stdio_scanf_s(benchmark::State& state) {
217 while (state.KeepRunning()) {
218 char s[BUFSIZ];
219 if (sscanf("file /etc/passwd", "file %s", s) != 1) abort();
220 }
221}
222BIONIC_BENCHMARK(BM_stdio_scanf_s);
223
224static void BM_stdio_scanf_d(benchmark::State& state) {
225 while (state.KeepRunning()) {
226 int i;
227 if (sscanf("size 12345", "size %d", &i) != 1) abort();
228 }
229}
230BIONIC_BENCHMARK(BM_stdio_scanf_d);
231
Elliott Hughes7063a832017-12-19 08:55:40 -0800232// Parsing maps is a common use of sscanf with a relatively complex format string.
Elliott Hughes3b644e92017-12-11 14:43:52 -0800233static void BM_stdio_scanf_maps(benchmark::State& state) {
234 while (state.KeepRunning()) {
235 uintptr_t start;
236 uintptr_t end;
237 uintptr_t offset;
238 char permissions[5];
239 int name_pos;
240 if (sscanf("6f000000-6f01e000 rwxp 00000000 00:0c 16389419 /system/lib/libcomposer.so",
241 "%" PRIxPTR "-%" PRIxPTR " %4s %" PRIxPTR " %*x:%*x %*d %n",
242 &start, &end, permissions, &offset, &name_pos) != 4) abort();
243 }
244}
245BIONIC_BENCHMARK(BM_stdio_scanf_maps);
Elliott Hughes7063a832017-12-19 08:55:40 -0800246
247// Hard-coded equivalent of the maps sscanf from libunwindstack/Maps.cpp for a baseline.
248static int ParseMap(const char* line, const char* /*fmt*/, uintptr_t* start, uintptr_t* end,
249 char* permissions, uintptr_t* offset, int* name_pos) __attribute__((noinline)) {
250 char* str;
251 const char* old_str = line;
252
253 // "%" PRIxPTR "-"
254 *start = strtoul(old_str, &str, 16);
255 if (old_str == str || *str++ != '-') return 0;
256
257 // "%" PRIxPTR " "
258 old_str = str;
259 *end = strtoul(old_str, &str, 16);
260 if (old_str == str || !std::isspace(*str++)) return 0;
261 while (std::isspace(*str)) str++;
262
263 // "%4s "
264 if (*str == '\0') return 0;
265 permissions[0] = *str;
266 str++;
267 permissions[1] = *str;
268 str++;
269 permissions[2] = *str;
270 str++;
271 permissions[3] = *str;
272 str++;
273 permissions[4] = 0;
274 if (!std::isspace(*str++)) return 0;
275
276 // "%" PRIxPTR " "
277 old_str = str;
278 *offset = strtoul(old_str, &str, 16);
279 if (old_str == str || !std::isspace(*str)) return 0;
280
281 // "%*x:%*x "
282 old_str = str;
283 (void)strtoul(old_str, &str, 16);
284 if (old_str == str || *str++ != ':') return 0;
285 if (std::isspace(*str)) return 0;
286 old_str = str;
287 (void)strtoul(str, &str, 16);
288 if (old_str == str || !std::isspace(*str++)) return 0;
289
290 // "%*d "
291 old_str = str;
292 (void)strtoul(old_str, &str, 10);
293 if (old_str == str || (!std::isspace(*str) && *str != '\0')) return 0;
294 while (std::isspace(*str)) str++;
295
296 // "%n"
297 *name_pos = (str - line);
298 return 4;
299}
300
301static void BM_stdio_scanf_maps_baseline(benchmark::State& state) {
302 while (state.KeepRunning()) {
303 uintptr_t start;
304 uintptr_t end;
305 uintptr_t offset;
306 char permissions[5];
307 int name_pos;
308 if (ParseMap("6f000000-6f01e000 rwxp 00000000 00:0c 16389419 /system/lib/libcomposer.so",
309 "%" PRIxPTR "-%" PRIxPTR " %4s %" PRIxPTR " %*x:%*x %*d %n",
310 &start, &end, permissions, &offset, &name_pos) != 4) abort();
311 }
312}
313BIONIC_BENCHMARK(BM_stdio_scanf_maps_baseline);