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 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> 38 void ReadWriteTest(benchmark::State& state, Fn f, bool buffered) { 39 size_t chunk_size = state.range(0); 40 41 FILE* fp = fopen("/dev/zero", "r+e"); 42 __fsetlocking(fp, FSETLOCKING_BYCALLER); 43 char* buf = new char[chunk_size]; 44 45 if (!buffered) { 46 setvbuf(fp, nullptr, _IONBF, 0); 47 } 48 49 while (state.KeepRunning()) { 50 if (f(buf, chunk_size, 1, fp) != 1) { 51 errx(1, "ERROR: op of %zu bytes failed.", chunk_size); 52 } 53 } 54 55 state.SetBytesProcessed(int64_t(state.iterations()) * int64_t(chunk_size)); 56 delete[] buf; 57 fclose(fp); 58 } 59 60 void BM_stdio_fread(benchmark::State& state) { 61 ReadWriteTest(state, fread, true); 62 } 63 BIONIC_BENCHMARK_WITH_ARG(BM_stdio_fread, "AT_COMMON_SIZES"); 64 65 void BM_stdio_fwrite(benchmark::State& state) { 66 ReadWriteTest(state, fwrite, true); 67 } 68 BIONIC_BENCHMARK_WITH_ARG(BM_stdio_fwrite, "AT_COMMON_SIZES"); 69 70 void BM_stdio_fread_unbuffered(benchmark::State& state) { 71 ReadWriteTest(state, fread, false); 72 } 73 BIONIC_BENCHMARK_WITH_ARG(BM_stdio_fread_unbuffered, "AT_COMMON_SIZES"); 74 75 void BM_stdio_fwrite_unbuffered(benchmark::State& state) { 76 ReadWriteTest(state, fwrite, false); 77 } 78 BIONIC_BENCHMARK_WITH_ARG(BM_stdio_fwrite_unbuffered, "AT_COMMON_SIZES"); 79 80 #if !defined(__GLIBC__) 81 static void FopenFgetlnFclose(benchmark::State& state, bool no_locking) { 82 TemporaryFile tf; 83 FillFile(tf); 84 while (state.KeepRunning()) { 85 FILE* fp = fopen(tf.path, "re"); 86 if (no_locking) __fsetlocking(fp, FSETLOCKING_BYCALLER); 87 size_t length; 88 while (fgetln(fp, &length) != nullptr) { 89 } 90 fclose(fp); 91 } 92 } 93 94 static void BM_stdio_fopen_fgetln_fclose_locking(benchmark::State& state) { 95 FopenFgetlnFclose(state, false); 96 } 97 BIONIC_BENCHMARK(BM_stdio_fopen_fgetln_fclose_locking); 98 99 void BM_stdio_fopen_fgetln_fclose_no_locking(benchmark::State& state) { 100 FopenFgetlnFclose(state, true); 101 } 102 BIONIC_BENCHMARK(BM_stdio_fopen_fgetln_fclose_no_locking); 103 #endif 104 105 static void FopenFgetsFclose(benchmark::State& state, bool no_locking) { 106 TemporaryFile tf; 107 FillFile(tf); 108 char buf[BUFSIZ]; 109 while (state.KeepRunning()) { 110 FILE* fp = fopen(tf.path, "re"); 111 if (no_locking) __fsetlocking(fp, FSETLOCKING_BYCALLER); 112 while (fgets(buf, sizeof(buf), fp) != nullptr) { 113 } 114 fclose(fp); 115 } 116 } 117 118 static void BM_stdio_fopen_fgets_fclose_locking(benchmark::State& state) { 119 FopenFgetsFclose(state, false); 120 } 121 BIONIC_BENCHMARK(BM_stdio_fopen_fgets_fclose_locking); 122 123 void BM_stdio_fopen_fgets_fclose_no_locking(benchmark::State& state) { 124 FopenFgetsFclose(state, true); 125 } 126 BIONIC_BENCHMARK(BM_stdio_fopen_fgets_fclose_no_locking); 127 128 static void FopenGetlineFclose(benchmark::State& state, bool no_locking) { 129 TemporaryFile tf; 130 FillFile(tf); 131 while (state.KeepRunning()) { 132 FILE* fp = fopen(tf.path, "re"); 133 if (no_locking) __fsetlocking(fp, FSETLOCKING_BYCALLER); 134 char* line = nullptr; 135 size_t n = 0; 136 while (getline(&line, &n, fp) != -1) { 137 } 138 free(line); 139 fclose(fp); 140 } 141 } 142 143 static void BM_stdio_fopen_getline_fclose_locking(benchmark::State& state) { 144 FopenGetlineFclose(state, false); 145 } 146 BIONIC_BENCHMARK(BM_stdio_fopen_getline_fclose_locking); 147 148 void BM_stdio_fopen_getline_fclose_no_locking(benchmark::State& state) { 149 FopenGetlineFclose(state, true); 150 } 151 BIONIC_BENCHMARK(BM_stdio_fopen_getline_fclose_no_locking); 152 153 static void FopenFgetcFclose(benchmark::State& state, bool no_locking) { 154 size_t nbytes = state.range(0); 155 while (state.KeepRunning()) { 156 FILE* fp = fopen("/dev/zero", "re"); 157 if (no_locking) __fsetlocking(fp, FSETLOCKING_BYCALLER); 158 volatile int c __attribute__((unused)); 159 for (size_t i = 0; i < nbytes; ++i) { 160 c = fgetc(fp); 161 } 162 fclose(fp); 163 } 164 } 165 166 static void BM_stdio_fopen_fgetc_fclose_locking(benchmark::State& state) { 167 FopenFgetcFclose(state, false); 168 } 169 BIONIC_BENCHMARK_WITH_ARG(BM_stdio_fopen_fgetc_fclose_locking, "1024"); 170 171 void BM_stdio_fopen_fgetc_fclose_no_locking(benchmark::State& state) { 172 FopenFgetcFclose(state, true); 173 } 174 BIONIC_BENCHMARK_WITH_ARG(BM_stdio_fopen_fgetc_fclose_no_locking, "1024"); 175 176 static void BM_stdio_printf_literal(benchmark::State& state) { 177 while (state.KeepRunning()) { 178 char buf[BUFSIZ]; 179 snprintf(buf, sizeof(buf), "this is just a literal string with no format specifiers"); 180 } 181 } 182 BIONIC_BENCHMARK(BM_stdio_printf_literal); 183 184 static void BM_stdio_printf_s(benchmark::State& state) { 185 while (state.KeepRunning()) { 186 char buf[BUFSIZ]; 187 snprintf(buf, sizeof(buf), "this is a more typical error message with detail: %s", 188 "No such file or directory"); 189 } 190 } 191 BIONIC_BENCHMARK(BM_stdio_printf_s); 192 193 static void BM_stdio_printf_d(benchmark::State& state) { 194 while (state.KeepRunning()) { 195 char buf[BUFSIZ]; 196 snprintf(buf, sizeof(buf), "this is a more typical error message with detail: %d", 123456); 197 } 198 } 199 BIONIC_BENCHMARK(BM_stdio_printf_d); 200 201 static void BM_stdio_printf_1$s(benchmark::State& state) { 202 while (state.KeepRunning()) { 203 char buf[BUFSIZ]; 204 snprintf(buf, sizeof(buf), "this is a more typical error message with detail: %1$s", 205 "No such file or directory"); 206 } 207 } 208 BIONIC_BENCHMARK(BM_stdio_printf_1$s); 209 210 static void BM_stdio_scanf_s(benchmark::State& state) { 211 while (state.KeepRunning()) { 212 char s[BUFSIZ]; 213 if (sscanf("file /etc/passwd", "file %s", s) != 1) abort(); 214 } 215 } 216 BIONIC_BENCHMARK(BM_stdio_scanf_s); 217 218 static void BM_stdio_scanf_d(benchmark::State& state) { 219 while (state.KeepRunning()) { 220 int i; 221 if (sscanf("size 12345", "size %d", &i) != 1) abort(); 222 } 223 } 224 BIONIC_BENCHMARK(BM_stdio_scanf_d); 225 226 // Parsing maps is a common use of sscanf with a relatively complex format string. 227 static void BM_stdio_scanf_maps(benchmark::State& state) { 228 while (state.KeepRunning()) { 229 uintptr_t start; 230 uintptr_t end; 231 uintptr_t offset; 232 char permissions[5]; 233 int name_pos; 234 if (sscanf("6f000000-6f01e000 rwxp 00000000 00:0c 16389419 /system/lib/libcomposer.so", 235 "%" PRIxPTR "-%" PRIxPTR " %4s %" PRIxPTR " %*x:%*x %*d %n", 236 &start, &end, permissions, &offset, &name_pos) != 4) abort(); 237 } 238 } 239 BIONIC_BENCHMARK(BM_stdio_scanf_maps); 240 241 // Hard-coded equivalent of the maps sscanf from libunwindstack/Maps.cpp for a baseline. 242 static int ParseMap(const char* line, const char* /*fmt*/, uintptr_t* start, uintptr_t* end, 243 char* permissions, uintptr_t* offset, int* name_pos) __attribute__((noinline)) { 244 char* str; 245 const char* old_str = line; 246 247 // "%" PRIxPTR "-" 248 *start = strtoul(old_str, &str, 16); 249 if (old_str == str || *str++ != '-') return 0; 250 251 // "%" PRIxPTR " " 252 old_str = str; 253 *end = strtoul(old_str, &str, 16); 254 if (old_str == str || !std::isspace(*str++)) return 0; 255 while (std::isspace(*str)) str++; 256 257 // "%4s " 258 if (*str == '\0') return 0; 259 permissions[0] = *str; 260 str++; 261 permissions[1] = *str; 262 str++; 263 permissions[2] = *str; 264 str++; 265 permissions[3] = *str; 266 str++; 267 permissions[4] = 0; 268 if (!std::isspace(*str++)) return 0; 269 270 // "%" PRIxPTR " " 271 old_str = str; 272 *offset = strtoul(old_str, &str, 16); 273 if (old_str == str || !std::isspace(*str)) return 0; 274 275 // "%*x:%*x " 276 old_str = str; 277 (void)strtoul(old_str, &str, 16); 278 if (old_str == str || *str++ != ':') return 0; 279 if (std::isspace(*str)) return 0; 280 old_str = str; 281 (void)strtoul(str, &str, 16); 282 if (old_str == str || !std::isspace(*str++)) return 0; 283 284 // "%*d " 285 old_str = str; 286 (void)strtoul(old_str, &str, 10); 287 if (old_str == str || (!std::isspace(*str) && *str != '\0')) return 0; 288 while (std::isspace(*str)) str++; 289 290 // "%n" 291 *name_pos = (str - line); 292 return 4; 293 } 294 295 static void BM_stdio_scanf_maps_baseline(benchmark::State& state) { 296 while (state.KeepRunning()) { 297 uintptr_t start; 298 uintptr_t end; 299 uintptr_t offset; 300 char permissions[5]; 301 int name_pos; 302 if (ParseMap("6f000000-6f01e000 rwxp 00000000 00:0c 16389419 /system/lib/libcomposer.so", 303 "%" PRIxPTR "-%" PRIxPTR " %4s %" PRIxPTR " %*x:%*x %*d %n", 304 &start, &end, permissions, &offset, &name_pos) != 4) abort(); 305 } 306 } 307 BIONIC_BENCHMARK(BM_stdio_scanf_maps_baseline); 308