• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
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 
17 #include <err.h>
18 #include <inttypes.h>
19 #include <stdio.h>
20 #include <stdio_ext.h>
21 #include <stdlib.h>
22 
23 #include <android-base/file.h>
24 #include <benchmark/benchmark.h>
25 #include "util.h"
26 
FillFile(TemporaryFile & tf)27 static void FillFile(TemporaryFile& tf) {
28   char line[256];
29   memset(line, 'x', sizeof(line));
30   line[sizeof(line) - 1] = '\0';
31 
32   FILE* fp = fopen(tf.path, "we");
33   for (size_t i = 0; i < 4096; ++i) fputs(line, fp);
34   fclose(fp);
35 }
36 
37 template <typename Fn>
ReadWriteTest(benchmark::State & state,Fn f,bool buffered)38 void ReadWriteTest(benchmark::State& state, Fn f, bool buffered) {
39   size_t chunk_size = state.range(0);
40 
41   // /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.)
48   FILE* fp = fopen("/dev/zero", "r+e");
49   __fsetlocking(fp, FSETLOCKING_BYCALLER);
50   char* buf = new char[chunk_size];
51 
52   if (!buffered) {
53     setvbuf(fp, nullptr, _IONBF, 0);
54   }
55 
56   while (state.KeepRunning()) {
57     if (f(buf, chunk_size, 1, fp) != 1) {
58       errx(1, "ERROR: op of %zu bytes failed.", chunk_size);
59     }
60   }
61 
62   state.SetBytesProcessed(int64_t(state.iterations()) * int64_t(chunk_size));
63   delete[] buf;
64   fclose(fp);
65 }
66 
BM_stdio_fread(benchmark::State & state)67 void BM_stdio_fread(benchmark::State& state) {
68   ReadWriteTest(state, fread, true);
69 }
70 BIONIC_BENCHMARK_WITH_ARG(BM_stdio_fread, "AT_COMMON_SIZES");
71 
BM_stdio_fwrite(benchmark::State & state)72 void BM_stdio_fwrite(benchmark::State& state) {
73   ReadWriteTest(state, fwrite, true);
74 }
75 BIONIC_BENCHMARK_WITH_ARG(BM_stdio_fwrite, "AT_COMMON_SIZES");
76 
BM_stdio_fread_unbuffered(benchmark::State & state)77 void BM_stdio_fread_unbuffered(benchmark::State& state) {
78   ReadWriteTest(state, fread, false);
79 }
80 BIONIC_BENCHMARK_WITH_ARG(BM_stdio_fread_unbuffered, "AT_COMMON_SIZES");
81 
BM_stdio_fwrite_unbuffered(benchmark::State & state)82 void BM_stdio_fwrite_unbuffered(benchmark::State& state) {
83   ReadWriteTest(state, fwrite, false);
84 }
85 BIONIC_BENCHMARK_WITH_ARG(BM_stdio_fwrite_unbuffered, "AT_COMMON_SIZES");
86 
87 #if !defined(__GLIBC__)
FopenFgetlnFclose(benchmark::State & state,bool no_locking)88 static void FopenFgetlnFclose(benchmark::State& state, bool no_locking) {
89   TemporaryFile tf;
90   FillFile(tf);
91   while (state.KeepRunning()) {
92     FILE* fp = fopen(tf.path, "re");
93     if (no_locking) __fsetlocking(fp, FSETLOCKING_BYCALLER);
94     size_t length;
95     while (fgetln(fp, &length) != nullptr) {
96     }
97     fclose(fp);
98   }
99 }
100 
BM_stdio_fopen_fgetln_fclose_locking(benchmark::State & state)101 static void BM_stdio_fopen_fgetln_fclose_locking(benchmark::State& state) {
102   FopenFgetlnFclose(state, false);
103 }
104 BIONIC_BENCHMARK(BM_stdio_fopen_fgetln_fclose_locking);
105 
BM_stdio_fopen_fgetln_fclose_no_locking(benchmark::State & state)106 void BM_stdio_fopen_fgetln_fclose_no_locking(benchmark::State& state) {
107   FopenFgetlnFclose(state, true);
108 }
109 BIONIC_BENCHMARK(BM_stdio_fopen_fgetln_fclose_no_locking);
110 #endif
111 
FopenFgetsFclose(benchmark::State & state,bool no_locking)112 static 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) {
120     }
121     fclose(fp);
122   }
123 }
124 
BM_stdio_fopen_fgets_fclose_locking(benchmark::State & state)125 static void BM_stdio_fopen_fgets_fclose_locking(benchmark::State& state) {
126   FopenFgetsFclose(state, false);
127 }
128 BIONIC_BENCHMARK(BM_stdio_fopen_fgets_fclose_locking);
129 
BM_stdio_fopen_fgets_fclose_no_locking(benchmark::State & state)130 void BM_stdio_fopen_fgets_fclose_no_locking(benchmark::State& state) {
131   FopenFgetsFclose(state, true);
132 }
133 BIONIC_BENCHMARK(BM_stdio_fopen_fgets_fclose_no_locking);
134 
FopenGetlineFclose(benchmark::State & state,bool no_locking)135 static 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 
BM_stdio_fopen_getline_fclose_locking(benchmark::State & state)150 static void BM_stdio_fopen_getline_fclose_locking(benchmark::State& state) {
151   FopenGetlineFclose(state, false);
152 }
153 BIONIC_BENCHMARK(BM_stdio_fopen_getline_fclose_locking);
154 
BM_stdio_fopen_getline_fclose_no_locking(benchmark::State & state)155 void BM_stdio_fopen_getline_fclose_no_locking(benchmark::State& state) {
156   FopenGetlineFclose(state, true);
157 }
158 BIONIC_BENCHMARK(BM_stdio_fopen_getline_fclose_no_locking);
159 
FopenFgetcFclose(benchmark::State & state,bool no_locking)160 static 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);
165     for (size_t i = 0; i < nbytes; ++i) {
166       benchmark::DoNotOptimize(fgetc(fp));
167     }
168     fclose(fp);
169   }
170 }
171 
BM_stdio_fopen_fgetc_fclose_locking(benchmark::State & state)172 static void BM_stdio_fopen_fgetc_fclose_locking(benchmark::State& state) {
173   FopenFgetcFclose(state, false);
174 }
175 BIONIC_BENCHMARK_WITH_ARG(BM_stdio_fopen_fgetc_fclose_locking, "1024");
176 
BM_stdio_fopen_fgetc_fclose_no_locking(benchmark::State & state)177 void BM_stdio_fopen_fgetc_fclose_no_locking(benchmark::State& state) {
178   FopenFgetcFclose(state, true);
179 }
180 BIONIC_BENCHMARK_WITH_ARG(BM_stdio_fopen_fgetc_fclose_no_locking, "1024");
181 
BM_stdio_printf_literal(benchmark::State & state)182 static 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 }
188 BIONIC_BENCHMARK(BM_stdio_printf_literal);
189 
BM_stdio_printf_s(benchmark::State & state)190 static 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 }
197 BIONIC_BENCHMARK(BM_stdio_printf_s);
198 
BM_stdio_printf_d(benchmark::State & state)199 static 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 }
205 BIONIC_BENCHMARK(BM_stdio_printf_d);
206 
BM_stdio_printf_1$s(benchmark::State & state)207 static 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 }
214 BIONIC_BENCHMARK(BM_stdio_printf_1$s);
215 
BM_stdio_scanf_s(benchmark::State & state)216 static 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 }
222 BIONIC_BENCHMARK(BM_stdio_scanf_s);
223 
BM_stdio_scanf_d(benchmark::State & state)224 static 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 }
230 BIONIC_BENCHMARK(BM_stdio_scanf_d);
231 
232 // Parsing maps is a common use of sscanf with a relatively complex format string.
BM_stdio_scanf_maps(benchmark::State & state)233 static 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 }
245 BIONIC_BENCHMARK(BM_stdio_scanf_maps);
246 
247 // Hard-coded equivalent of the maps sscanf from libunwindstack/Maps.cpp for a baseline.
ParseMap(const char * line,const char *,uintptr_t * start,uintptr_t * end,char * permissions,uintptr_t * offset,int * name_pos)248 static 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 
BM_stdio_scanf_maps_baseline(benchmark::State & state)301 static 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 }
313 BIONIC_BENCHMARK(BM_stdio_scanf_maps_baseline);
314