StringBenchmark.cpp 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317
  1. /*
  2. * Copyright 2014-present Facebook, Inc.
  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. #include <folly/String.h>
  17. #include <boost/algorithm/string.hpp>
  18. #include <folly/Benchmark.h>
  19. #include <folly/Random.h>
  20. #include <random>
  21. using namespace folly;
  22. using namespace std;
  23. BENCHMARK(libc_tolower, iters) {
  24. static const size_t kSize = 256;
  25. // This array is static to keep the compiler from optimizing the
  26. // entire function down to a no-op if it has an inlined impl of
  27. // tolower and thus is able to tell that there are no side-effects.
  28. // No side-effects + no writes to anything other than local variables
  29. // + no return value = no need to run any of the code in the function.
  30. // gcc, for example, makes that optimization with -O2.
  31. static char input[kSize];
  32. for (size_t i = 0; i < kSize; i++) {
  33. input[i] = (char)(i & 0xff);
  34. }
  35. for (auto i = iters; i > 0; i--) {
  36. for (size_t offset = 0; offset < kSize; offset++) {
  37. input[offset] = tolower(input[offset]);
  38. }
  39. }
  40. }
  41. BENCHMARK(folly_toLowerAscii, iters) {
  42. static const size_t kSize = 256;
  43. static char input[kSize];
  44. for (size_t i = 0; i < kSize; i++) {
  45. input[i] = (char)(i & 0xff);
  46. }
  47. for (auto i = iters; i > 0; i--) {
  48. folly::toLowerAscii(input, kSize);
  49. }
  50. }
  51. // A simple benchmark that tests various output sizes for a simple
  52. // input; the goal is to measure the output buffer resize code cost.
  53. void stringPrintfOutputSize(int iters, int param) {
  54. string buffer;
  55. BENCHMARK_SUSPEND {
  56. buffer.resize(param, 'x');
  57. }
  58. for (int64_t i = 0; i < iters; ++i) {
  59. string s = stringPrintf("msg: %d, %d, %s", 10, 20, buffer.c_str());
  60. }
  61. }
  62. // The first few of these tend to fit in the inline buffer, while the
  63. // subsequent ones cross that limit, trigger a second vsnprintf, and
  64. // exercise a different codepath.
  65. BENCHMARK_PARAM(stringPrintfOutputSize, 1)
  66. BENCHMARK_PARAM(stringPrintfOutputSize, 4)
  67. BENCHMARK_PARAM(stringPrintfOutputSize, 16)
  68. BENCHMARK_PARAM(stringPrintfOutputSize, 64)
  69. BENCHMARK_PARAM(stringPrintfOutputSize, 256)
  70. BENCHMARK_PARAM(stringPrintfOutputSize, 1024)
  71. // Benchmark simple stringAppendf behavior to show a pathology Lovro
  72. // reported (t5735468).
  73. BENCHMARK(stringPrintfAppendfBenchmark, iters) {
  74. for (unsigned int i = 0; i < iters; ++i) {
  75. string s;
  76. BENCHMARK_SUSPEND {
  77. s.reserve(300000);
  78. }
  79. for (int j = 0; j < 300000; ++j) {
  80. stringAppendf(&s, "%d", 1);
  81. }
  82. }
  83. }
  84. namespace {
  85. fbstring cbmString;
  86. fbstring cbmEscapedString;
  87. fbstring cEscapedString;
  88. fbstring cUnescapedString;
  89. const size_t kCBmStringLength = 64 << 10;
  90. const uint32_t kCPrintablePercentage = 90;
  91. fbstring uribmString;
  92. fbstring uribmEscapedString;
  93. fbstring uriEscapedString;
  94. fbstring uriUnescapedString;
  95. const size_t kURIBmStringLength = 256;
  96. const uint32_t kURIPassThroughPercentage = 50;
  97. fbstring hexlifyInput;
  98. fbstring hexlifyOutput;
  99. const size_t kHexlifyLength = 1024;
  100. void initBenchmark() {
  101. std::mt19937 rnd;
  102. // C escape
  103. std::uniform_int_distribution<uint32_t> printable(32, 126);
  104. std::uniform_int_distribution<uint32_t> nonPrintable(0, 160);
  105. std::uniform_int_distribution<uint32_t> percentage(0, 99);
  106. cbmString.reserve(kCBmStringLength);
  107. for (size_t i = 0; i < kCBmStringLength; ++i) {
  108. unsigned char c;
  109. if (percentage(rnd) < kCPrintablePercentage) {
  110. c = printable(rnd);
  111. } else {
  112. c = nonPrintable(rnd);
  113. // Generate characters in both non-printable ranges:
  114. // 0..31 and 127..255
  115. if (c >= 32) {
  116. c += (126 - 32) + 1;
  117. }
  118. }
  119. cbmString.push_back(c);
  120. }
  121. cbmEscapedString = cEscape<fbstring>(cbmString);
  122. // URI escape
  123. std::uniform_int_distribution<uint32_t> passthrough('a', 'z');
  124. std::string encodeChars = " ?!\"',+[]";
  125. std::uniform_int_distribution<uint32_t> encode(0, encodeChars.size() - 1);
  126. uribmString.reserve(kURIBmStringLength);
  127. for (size_t i = 0; i < kURIBmStringLength; ++i) {
  128. unsigned char c;
  129. if (percentage(rnd) < kURIPassThroughPercentage) {
  130. c = passthrough(rnd);
  131. } else {
  132. c = encodeChars[encode(rnd)];
  133. }
  134. uribmString.push_back(c);
  135. }
  136. uribmEscapedString = uriEscape<fbstring>(uribmString);
  137. // hexlify
  138. hexlifyInput.resize(kHexlifyLength);
  139. Random::secureRandom(&hexlifyInput[0], kHexlifyLength);
  140. folly::hexlify(hexlifyInput, hexlifyOutput);
  141. }
  142. BENCHMARK(BM_cEscape, iters) {
  143. while (iters--) {
  144. cEscapedString = cEscape<fbstring>(cbmString);
  145. doNotOptimizeAway(cEscapedString.size());
  146. }
  147. }
  148. BENCHMARK(BM_cUnescape, iters) {
  149. while (iters--) {
  150. cUnescapedString = cUnescape<fbstring>(cbmEscapedString);
  151. doNotOptimizeAway(cUnescapedString.size());
  152. }
  153. }
  154. BENCHMARK(BM_uriEscape, iters) {
  155. while (iters--) {
  156. uriEscapedString = uriEscape<fbstring>(uribmString);
  157. doNotOptimizeAway(uriEscapedString.size());
  158. }
  159. }
  160. BENCHMARK(BM_uriUnescape, iters) {
  161. while (iters--) {
  162. uriUnescapedString = uriUnescape<fbstring>(uribmEscapedString);
  163. doNotOptimizeAway(uriUnescapedString.size());
  164. }
  165. }
  166. BENCHMARK(BM_unhexlify, iters) {
  167. // iters/sec = bytes output per sec
  168. std::string unhexed;
  169. folly::StringPiece hex = hexlifyOutput;
  170. for (; iters >= hex.size(); iters -= hex.size()) {
  171. folly::unhexlify(hex, unhexed);
  172. }
  173. iters -= iters % 2; // round down to an even number of chars
  174. hex = hex.subpiece(0, iters);
  175. folly::unhexlify(hex, unhexed);
  176. }
  177. } // namespace
  178. //////////////////////////////////////////////////////////////////////
  179. BENCHMARK(splitOnSingleChar, iters) {
  180. static const std::string line = "one:two:three:four";
  181. for (size_t i = 0; i < iters << 4; ++i) {
  182. std::vector<StringPiece> pieces;
  183. folly::split(':', line, pieces);
  184. }
  185. }
  186. BENCHMARK(splitOnSingleCharFixed, iters) {
  187. static const std::string line = "one:two:three:four";
  188. for (size_t i = 0; i < iters << 4; ++i) {
  189. StringPiece a, b, c, d;
  190. folly::split(':', line, a, b, c, d);
  191. }
  192. }
  193. BENCHMARK(splitOnSingleCharFixedAllowExtra, iters) {
  194. static const std::string line = "one:two:three:four";
  195. for (size_t i = 0; i < iters << 4; ++i) {
  196. StringPiece a, b, c, d;
  197. folly::split<false>(':', line, a, b, c, d);
  198. }
  199. }
  200. BENCHMARK(splitStr, iters) {
  201. static const std::string line = "one-*-two-*-three-*-four";
  202. for (size_t i = 0; i < iters << 4; ++i) {
  203. std::vector<StringPiece> pieces;
  204. folly::split("-*-", line, pieces);
  205. }
  206. }
  207. BENCHMARK(splitStrFixed, iters) {
  208. static const std::string line = "one-*-two-*-three-*-four";
  209. for (size_t i = 0; i < iters << 4; ++i) {
  210. StringPiece a, b, c, d;
  211. folly::split("-*-", line, a, b, c, d);
  212. }
  213. }
  214. BENCHMARK(boost_splitOnSingleChar, iters) {
  215. static const std::string line = "one:two:three:four";
  216. bool (*pred)(char) = [](char c) -> bool { return c == ':'; };
  217. for (size_t i = 0; i < iters << 4; ++i) {
  218. std::vector<boost::iterator_range<std::string::const_iterator>> pieces;
  219. boost::split(pieces, line, pred);
  220. }
  221. }
  222. BENCHMARK(joinCharStr, iters) {
  223. static const std::vector<std::string> input = {
  224. "one", "two", "three", "four", "five", "six", "seven"};
  225. for (size_t i = 0; i < iters << 4; ++i) {
  226. std::string output;
  227. folly::join(':', input, output);
  228. }
  229. }
  230. BENCHMARK(joinStrStr, iters) {
  231. static const std::vector<std::string> input = {
  232. "one", "two", "three", "four", "five", "six", "seven"};
  233. for (size_t i = 0; i < iters << 4; ++i) {
  234. std::string output;
  235. folly::join(":", input, output);
  236. }
  237. }
  238. BENCHMARK(joinInt, iters) {
  239. static const auto input = {123, 456, 78910, 1112, 1314, 151, 61718};
  240. for (size_t i = 0; i < iters << 4; ++i) {
  241. std::string output;
  242. folly::join(":", input, output);
  243. }
  244. }
  245. int main(int argc, char** argv) {
  246. gflags::ParseCommandLineFlags(&argc, &argv, true);
  247. initBenchmark();
  248. folly::runBenchmarks();
  249. return 0;
  250. }
  251. /*
  252. Results on x86_64:
  253. ============================================================================
  254. folly/test/StringBenchmark.cpp relative time/iter iters/s
  255. ============================================================================
  256. libc_tolower 773.30ns 1.29M
  257. folly_toLowerAscii 65.04ns 15.38M
  258. stringPrintfOutputSize(1) 224.67ns 4.45M
  259. stringPrintfOutputSize(4) 231.53ns 4.32M
  260. stringPrintfOutputSize(16) 286.54ns 3.49M
  261. stringPrintfOutputSize(64) 305.47ns 3.27M
  262. stringPrintfOutputSize(256) 1.48us 674.45K
  263. stringPrintfOutputSize(1024) 5.89us 169.72K
  264. stringPrintfAppendfBenchmark 34.43ms 29.04
  265. BM_cEscape 461.51us 2.17K
  266. BM_cUnescape 328.19us 3.05K
  267. BM_uriEscape 4.36us 229.25K
  268. BM_uriUnescape 2.22us 450.64K
  269. splitOnSingleChar 1.46us 687.21K
  270. splitOnSingleCharFixed 133.02ns 7.52M
  271. splitOnSingleCharFixedAllowExtra 74.35ns 13.45M
  272. splitStr 2.36us 424.00K
  273. splitStrFixed 282.38ns 3.54M
  274. boost_splitOnSingleChar 2.83us 353.12K
  275. joinCharStr 2.65us 376.93K
  276. joinStrStr 2.64us 378.09K
  277. joinInt 3.89us 257.37K
  278. ============================================================================
  279. */