memutil_benchmark.cc
Go to the documentation of this file.
00001 // Copyright 2018 The Abseil Authors.
00002 //
00003 // Licensed under the Apache License, Version 2.0 (the "License");
00004 // you may not use this file except in compliance with the License.
00005 // You may obtain a copy of the License at
00006 //
00007 //      https://www.apache.org/licenses/LICENSE-2.0
00008 //
00009 // Unless required by applicable law or agreed to in writing, software
00010 // distributed under the License is distributed on an "AS IS" BASIS,
00011 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
00012 // See the License for the specific language governing permissions and
00013 // limitations under the License.
00014 
00015 #include "absl/strings/internal/memutil.h"
00016 
00017 #include <algorithm>
00018 #include <cstdlib>
00019 
00020 #include "benchmark/benchmark.h"
00021 #include "absl/strings/ascii.h"
00022 
00023 // We fill the haystack with aaaaaaaaaaaaaaaaaa...aaaab.
00024 // That gives us:
00025 // - an easy search: 'b'
00026 // - a medium search: 'ab'.  That means every letter is a possible match.
00027 // - a pathological search: 'aaaaaa.......aaaaab' (half as many a's as haytack)
00028 // We benchmark case-sensitive and case-insensitive versions of
00029 // three memmem implementations:
00030 // - memmem() from memutil.h
00031 // - search() from STL
00032 // - memmatch(), a custom implementation using memchr and memcmp.
00033 // Here are sample results:
00034 //
00035 // Run on (12 X 3800 MHz CPU s)
00036 // CPU Caches:
00037 //   L1 Data 32K (x6)
00038 //   L1 Instruction 32K (x6)
00039 //   L2 Unified 256K (x6)
00040 //   L3 Unified 15360K (x1)
00041 // ----------------------------------------------------------------
00042 // Benchmark                           Time          CPU Iterations
00043 // ----------------------------------------------------------------
00044 // BM_Memmem                        3583 ns      3582 ns     196469  2.59966GB/s
00045 // BM_MemmemMedium                 13743 ns     13742 ns      50901  693.986MB/s
00046 // BM_MemmemPathological        13695030 ns  13693977 ns         51  713.133kB/s
00047 // BM_Memcasemem                    3299 ns      3299 ns     212942  2.82309GB/s
00048 // BM_MemcasememMedium             16407 ns     16406 ns      42170  581.309MB/s
00049 // BM_MemcasememPathological    17267745 ns  17266030 ns         41  565.598kB/s
00050 // BM_Search                        1610 ns      1609 ns     431321  5.78672GB/s
00051 // BM_SearchMedium                 11111 ns     11110 ns      63001  858.414MB/s
00052 // BM_SearchPathological        12117390 ns  12116397 ns         58  805.984kB/s
00053 // BM_Searchcase                    3081 ns      3081 ns     229949  3.02313GB/s
00054 // BM_SearchcaseMedium             16003 ns     16001 ns      44170  595.998MB/s
00055 // BM_SearchcasePathological    15823413 ns  15821909 ns         44  617.222kB/s
00056 // BM_Memmatch                       197 ns       197 ns    3584225  47.2951GB/s
00057 // BM_MemmatchMedium               52333 ns     52329 ns      13280  182.244MB/s
00058 // BM_MemmatchPathological        659799 ns    659727 ns       1058  14.4556MB/s
00059 // BM_Memcasematch                  5460 ns      5460 ns     127606  1.70586GB/s
00060 // BM_MemcasematchMedium           32861 ns     32857 ns      21258  290.248MB/s
00061 // BM_MemcasematchPathological  15154243 ns  15153089 ns         46  644.464kB/s
00062 // BM_MemmemStartup                    5 ns         5 ns  150821500
00063 // BM_SearchStartup                    5 ns         5 ns  150644203
00064 // BM_MemmatchStartup                  7 ns         7 ns   97068802
00065 //
00066 // Conclusions:
00067 //
00068 // The following recommendations are based on the sample results above. However,
00069 // we have found that the performance of STL search can vary significantly
00070 // depending on compiler and standard library implementation. We recommend you
00071 // run the benchmarks for yourself on relevant platforms.
00072 //
00073 // If you need case-insensitive, STL search is slightly better than memmem for
00074 // all cases.
00075 //
00076 // Case-sensitive is more subtle:
00077 // Custom memmatch is _very_ fast at scanning, so if you have very few possible
00078 // matches in your haystack, that's the way to go. Performance drops
00079 // significantly with more matches.
00080 //
00081 // STL search is slightly faster than memmem in the medium and pathological
00082 // benchmarks. However, the performance of memmem is currently more dependable
00083 // across platforms and build configurations.
00084 
00085 namespace {
00086 
00087 constexpr int kHaystackSize = 10000;
00088 constexpr int64_t kHaystackSize64 = kHaystackSize;
00089 const char* MakeHaystack() {
00090   char* haystack = new char[kHaystackSize];
00091   for (int i = 0; i < kHaystackSize - 1; ++i) haystack[i] = 'a';
00092   haystack[kHaystackSize - 1] = 'b';
00093   return haystack;
00094 }
00095 const char* const kHaystack = MakeHaystack();
00096 
00097 void BM_Memmem(benchmark::State& state) {
00098   for (auto _ : state) {
00099     benchmark::DoNotOptimize(
00100         absl::strings_internal::memmem(kHaystack, kHaystackSize, "b", 1));
00101   }
00102   state.SetBytesProcessed(kHaystackSize64 * state.iterations());
00103 }
00104 BENCHMARK(BM_Memmem);
00105 
00106 void BM_MemmemMedium(benchmark::State& state) {
00107   for (auto _ : state) {
00108     benchmark::DoNotOptimize(
00109         absl::strings_internal::memmem(kHaystack, kHaystackSize, "ab", 2));
00110   }
00111   state.SetBytesProcessed(kHaystackSize64 * state.iterations());
00112 }
00113 BENCHMARK(BM_MemmemMedium);
00114 
00115 void BM_MemmemPathological(benchmark::State& state) {
00116   for (auto _ : state) {
00117     benchmark::DoNotOptimize(absl::strings_internal::memmem(
00118         kHaystack, kHaystackSize, kHaystack + kHaystackSize / 2,
00119         kHaystackSize - kHaystackSize / 2));
00120   }
00121   state.SetBytesProcessed(kHaystackSize64 * state.iterations());
00122 }
00123 BENCHMARK(BM_MemmemPathological);
00124 
00125 void BM_Memcasemem(benchmark::State& state) {
00126   for (auto _ : state) {
00127     benchmark::DoNotOptimize(
00128         absl::strings_internal::memcasemem(kHaystack, kHaystackSize, "b", 1));
00129   }
00130   state.SetBytesProcessed(kHaystackSize64 * state.iterations());
00131 }
00132 BENCHMARK(BM_Memcasemem);
00133 
00134 void BM_MemcasememMedium(benchmark::State& state) {
00135   for (auto _ : state) {
00136     benchmark::DoNotOptimize(
00137         absl::strings_internal::memcasemem(kHaystack, kHaystackSize, "ab", 2));
00138   }
00139   state.SetBytesProcessed(kHaystackSize64 * state.iterations());
00140 }
00141 BENCHMARK(BM_MemcasememMedium);
00142 
00143 void BM_MemcasememPathological(benchmark::State& state) {
00144   for (auto _ : state) {
00145     benchmark::DoNotOptimize(absl::strings_internal::memcasemem(
00146         kHaystack, kHaystackSize, kHaystack + kHaystackSize / 2,
00147         kHaystackSize - kHaystackSize / 2));
00148   }
00149   state.SetBytesProcessed(kHaystackSize64 * state.iterations());
00150 }
00151 BENCHMARK(BM_MemcasememPathological);
00152 
00153 bool case_eq(const char a, const char b) {
00154   return absl::ascii_tolower(a) == absl::ascii_tolower(b);
00155 }
00156 
00157 void BM_Search(benchmark::State& state) {
00158   for (auto _ : state) {
00159     benchmark::DoNotOptimize(std::search(kHaystack, kHaystack + kHaystackSize,
00160                                          kHaystack + kHaystackSize - 1,
00161                                          kHaystack + kHaystackSize));
00162   }
00163   state.SetBytesProcessed(kHaystackSize64 * state.iterations());
00164 }
00165 BENCHMARK(BM_Search);
00166 
00167 void BM_SearchMedium(benchmark::State& state) {
00168   for (auto _ : state) {
00169     benchmark::DoNotOptimize(std::search(kHaystack, kHaystack + kHaystackSize,
00170                                          kHaystack + kHaystackSize - 2,
00171                                          kHaystack + kHaystackSize));
00172   }
00173   state.SetBytesProcessed(kHaystackSize64 * state.iterations());
00174 }
00175 BENCHMARK(BM_SearchMedium);
00176 
00177 void BM_SearchPathological(benchmark::State& state) {
00178   for (auto _ : state) {
00179     benchmark::DoNotOptimize(std::search(kHaystack, kHaystack + kHaystackSize,
00180                                          kHaystack + kHaystackSize / 2,
00181                                          kHaystack + kHaystackSize));
00182   }
00183   state.SetBytesProcessed(kHaystackSize64 * state.iterations());
00184 }
00185 BENCHMARK(BM_SearchPathological);
00186 
00187 void BM_Searchcase(benchmark::State& state) {
00188   for (auto _ : state) {
00189     benchmark::DoNotOptimize(std::search(kHaystack, kHaystack + kHaystackSize,
00190                                          kHaystack + kHaystackSize - 1,
00191                                          kHaystack + kHaystackSize, case_eq));
00192   }
00193   state.SetBytesProcessed(kHaystackSize64 * state.iterations());
00194 }
00195 BENCHMARK(BM_Searchcase);
00196 
00197 void BM_SearchcaseMedium(benchmark::State& state) {
00198   for (auto _ : state) {
00199     benchmark::DoNotOptimize(std::search(kHaystack, kHaystack + kHaystackSize,
00200                                          kHaystack + kHaystackSize - 2,
00201                                          kHaystack + kHaystackSize, case_eq));
00202   }
00203   state.SetBytesProcessed(kHaystackSize64 * state.iterations());
00204 }
00205 BENCHMARK(BM_SearchcaseMedium);
00206 
00207 void BM_SearchcasePathological(benchmark::State& state) {
00208   for (auto _ : state) {
00209     benchmark::DoNotOptimize(std::search(kHaystack, kHaystack + kHaystackSize,
00210                                          kHaystack + kHaystackSize / 2,
00211                                          kHaystack + kHaystackSize, case_eq));
00212   }
00213   state.SetBytesProcessed(kHaystackSize64 * state.iterations());
00214 }
00215 BENCHMARK(BM_SearchcasePathological);
00216 
00217 char* memcasechr(const char* s, int c, size_t slen) {
00218   c = absl::ascii_tolower(c);
00219   for (; slen; ++s, --slen) {
00220     if (absl::ascii_tolower(*s) == c) return const_cast<char*>(s);
00221   }
00222   return nullptr;
00223 }
00224 
00225 const char* memcasematch(const char* phaystack, size_t haylen,
00226                          const char* pneedle, size_t neelen) {
00227   if (0 == neelen) {
00228     return phaystack;  // even if haylen is 0
00229   }
00230   if (haylen < neelen) return nullptr;
00231 
00232   const char* match;
00233   const char* hayend = phaystack + haylen - neelen + 1;
00234   while ((match = static_cast<char*>(
00235               memcasechr(phaystack, pneedle[0], hayend - phaystack)))) {
00236     if (absl::strings_internal::memcasecmp(match, pneedle, neelen) == 0)
00237       return match;
00238     else
00239       phaystack = match + 1;
00240   }
00241   return nullptr;
00242 }
00243 
00244 void BM_Memmatch(benchmark::State& state) {
00245   for (auto _ : state) {
00246     benchmark::DoNotOptimize(
00247         absl::strings_internal::memmatch(kHaystack, kHaystackSize, "b", 1));
00248   }
00249   state.SetBytesProcessed(kHaystackSize64 * state.iterations());
00250 }
00251 BENCHMARK(BM_Memmatch);
00252 
00253 void BM_MemmatchMedium(benchmark::State& state) {
00254   for (auto _ : state) {
00255     benchmark::DoNotOptimize(
00256         absl::strings_internal::memmatch(kHaystack, kHaystackSize, "ab", 2));
00257   }
00258   state.SetBytesProcessed(kHaystackSize64 * state.iterations());
00259 }
00260 BENCHMARK(BM_MemmatchMedium);
00261 
00262 void BM_MemmatchPathological(benchmark::State& state) {
00263   for (auto _ : state) {
00264     benchmark::DoNotOptimize(absl::strings_internal::memmatch(
00265         kHaystack, kHaystackSize, kHaystack + kHaystackSize / 2,
00266         kHaystackSize - kHaystackSize / 2));
00267   }
00268   state.SetBytesProcessed(kHaystackSize64 * state.iterations());
00269 }
00270 BENCHMARK(BM_MemmatchPathological);
00271 
00272 void BM_Memcasematch(benchmark::State& state) {
00273   for (auto _ : state) {
00274     benchmark::DoNotOptimize(memcasematch(kHaystack, kHaystackSize, "b", 1));
00275   }
00276   state.SetBytesProcessed(kHaystackSize64 * state.iterations());
00277 }
00278 BENCHMARK(BM_Memcasematch);
00279 
00280 void BM_MemcasematchMedium(benchmark::State& state) {
00281   for (auto _ : state) {
00282     benchmark::DoNotOptimize(memcasematch(kHaystack, kHaystackSize, "ab", 2));
00283   }
00284   state.SetBytesProcessed(kHaystackSize64 * state.iterations());
00285 }
00286 BENCHMARK(BM_MemcasematchMedium);
00287 
00288 void BM_MemcasematchPathological(benchmark::State& state) {
00289   for (auto _ : state) {
00290     benchmark::DoNotOptimize(memcasematch(kHaystack, kHaystackSize,
00291                                           kHaystack + kHaystackSize / 2,
00292                                           kHaystackSize - kHaystackSize / 2));
00293   }
00294   state.SetBytesProcessed(kHaystackSize64 * state.iterations());
00295 }
00296 BENCHMARK(BM_MemcasematchPathological);
00297 
00298 void BM_MemmemStartup(benchmark::State& state) {
00299   for (auto _ : state) {
00300     benchmark::DoNotOptimize(absl::strings_internal::memmem(
00301         kHaystack + kHaystackSize - 10, 10, kHaystack + kHaystackSize - 1, 1));
00302   }
00303 }
00304 BENCHMARK(BM_MemmemStartup);
00305 
00306 void BM_SearchStartup(benchmark::State& state) {
00307   for (auto _ : state) {
00308     benchmark::DoNotOptimize(
00309         std::search(kHaystack + kHaystackSize - 10, kHaystack + kHaystackSize,
00310                     kHaystack + kHaystackSize - 1, kHaystack + kHaystackSize));
00311   }
00312 }
00313 BENCHMARK(BM_SearchStartup);
00314 
00315 void BM_MemmatchStartup(benchmark::State& state) {
00316   for (auto _ : state) {
00317     benchmark::DoNotOptimize(absl::strings_internal::memmatch(
00318         kHaystack + kHaystackSize - 10, 10, kHaystack + kHaystackSize - 1, 1));
00319   }
00320 }
00321 BENCHMARK(BM_MemmatchStartup);
00322 
00323 }  // namespace


abseil_cpp
Author(s):
autogenerated on Wed Jun 19 2019 19:42:15