Home | History | Annotate | Download | only in benchmarks
      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/test_utils.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, 0, _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