RangeFindBenchmark.cpp 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409
  1. /*
  2. * Copyright 2012-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/Range.h>
  17. #include <algorithm>
  18. #include <iostream>
  19. #include <random>
  20. #include <string>
  21. #include <folly/Benchmark.h>
  22. #include <folly/container/Foreach.h>
  23. using namespace folly;
  24. using namespace std;
  25. namespace {
  26. std::string str;
  27. constexpr int kVstrSize = 16;
  28. std::vector<std::string> vstr;
  29. std::vector<StringPiece> vstrp;
  30. std::string file;
  31. void initStr(int len) {
  32. str.clear();
  33. vstr.clear();
  34. vstrp.clear();
  35. str.reserve(len + 1);
  36. str.append(len, 'a');
  37. str.append(1, 'b');
  38. // create 16 copies of str, each with a different 16byte alignment.
  39. // Useful because some implementations of find_first_of have different
  40. // behaviors based on byte alignment.
  41. for (int i = 0; i < kVstrSize; ++i) {
  42. string s(i, '$');
  43. s += str;
  44. if (i % 2) {
  45. // some find_first_of implementations have special (page-safe) logic
  46. // for handling the end of a string. Flex that logic only sometimes.
  47. s += string(32, '$');
  48. }
  49. vstr.push_back(s);
  50. StringPiece sp(vstr.back());
  51. sp.advance(i);
  52. vstrp.push_back(sp);
  53. }
  54. }
  55. std::mt19937 rnd;
  56. string ffoTestString;
  57. const size_t ffoDelimSize = 128;
  58. vector<string> ffoDelim;
  59. void initFile(int len) {
  60. std::uniform_int_distribution<uint32_t> validChar(1, 64);
  61. file.clear();
  62. while (len--) {
  63. char ch = validChar(rnd);
  64. if (ch == '\r') {
  65. ch = '\n';
  66. }
  67. file.push_back(ch);
  68. }
  69. }
  70. string generateString(int len) {
  71. std::uniform_int_distribution<uint32_t> validChar(1, 255); // no null-char
  72. string ret;
  73. while (len--) {
  74. ret.push_back(validChar(rnd));
  75. }
  76. return ret;
  77. }
  78. void initDelims(int len) {
  79. ffoDelim.clear();
  80. string s(len - 1, '\0'); // find_first_of won't finish until last char
  81. s.push_back('a');
  82. ffoTestString = s;
  83. for (size_t i = 0; i < ffoDelimSize; ++i) {
  84. // most delimiter sets are pretty small, but occasionally there could
  85. // be a big one.
  86. auto n = rnd() % 8 + 1;
  87. if (n == 8) {
  88. n = 32;
  89. }
  90. auto s_ = generateString(n);
  91. if (rnd() % 2) {
  92. // ~half of tests will find a hit
  93. s_[rnd() % s_.size()] = 'a'; // yes, this could mean 'a' is a duplicate
  94. }
  95. ffoDelim.push_back(s_);
  96. }
  97. }
  98. } // namespace
  99. BENCHMARK(FindSingleCharMemchr, n) {
  100. StringPiece haystack(str);
  101. FOR_EACH_RANGE (i, 0, n) {
  102. doNotOptimizeAway(haystack.find('b'));
  103. char x = haystack[0];
  104. doNotOptimizeAway(&x);
  105. }
  106. }
  107. BENCHMARK_RELATIVE(FindSingleCharRange, n) {
  108. const char c = 'b';
  109. StringPiece haystack(str);
  110. folly::StringPiece needle(&c, &c + 1);
  111. FOR_EACH_RANGE (i, 0, n) {
  112. doNotOptimizeAway(haystack.find(needle));
  113. char x = haystack[0];
  114. doNotOptimizeAway(&x);
  115. }
  116. }
  117. BENCHMARK_DRAW_LINE();
  118. template <class Func>
  119. void countHits(Func func, size_t n) {
  120. StringPiece needles = "\r\n\1";
  121. FOR_EACH_RANGE (i, 0, n) {
  122. size_t p, c = 0;
  123. for (StringPiece left = file;
  124. (p = func(left, needles)) != StringPiece::npos;
  125. left.advance(p + 1)) {
  126. ++c;
  127. }
  128. doNotOptimizeAway(c);
  129. }
  130. }
  131. template <class Func>
  132. void findFirstOfRange(StringPiece needles, Func func, size_t n) {
  133. FOR_EACH_RANGE (i, 0, n) {
  134. const StringPiece haystack = vstrp[i % kVstrSize];
  135. doNotOptimizeAway(func(haystack, needles));
  136. char x = haystack[0];
  137. doNotOptimizeAway(&x);
  138. }
  139. }
  140. const string delims1 = "b";
  141. BENCHMARK(FindFirstOf1NeedlesBase, n) {
  142. findFirstOfRange(delims1, detail::qfind_first_byte_of, n);
  143. }
  144. BENCHMARK_RELATIVE(FindFirstOf1NeedlesNoSSE, n) {
  145. findFirstOfRange(delims1, detail::qfind_first_byte_of_nosse, n);
  146. }
  147. BENCHMARK_RELATIVE(FindFirstOf1NeedlesStd, n) {
  148. findFirstOfRange(delims1, detail::qfind_first_byte_of_std, n);
  149. }
  150. BENCHMARK_RELATIVE(FindFirstOf1NeedlesByteSet, n) {
  151. findFirstOfRange(delims1, detail::qfind_first_byte_of_byteset, n);
  152. }
  153. BENCHMARK_RELATIVE(FindFirstOf1NeedlesBitSet, n) {
  154. findFirstOfRange(delims1, detail::qfind_first_byte_of_bitset, n);
  155. }
  156. BENCHMARK_DRAW_LINE();
  157. const string delims2 = "bc";
  158. BENCHMARK(FindFirstOf2NeedlesBase, n) {
  159. findFirstOfRange(delims2, detail::qfind_first_byte_of, n);
  160. }
  161. BENCHMARK_RELATIVE(FindFirstOf2NeedlesNoSSE, n) {
  162. findFirstOfRange(delims2, detail::qfind_first_byte_of_nosse, n);
  163. }
  164. BENCHMARK_RELATIVE(FindFirstOf2NeedlesStd, n) {
  165. findFirstOfRange(delims2, detail::qfind_first_byte_of_std, n);
  166. }
  167. BENCHMARK_RELATIVE(FindFirstOf2NeedlesByteSet, n) {
  168. findFirstOfRange(delims2, detail::qfind_first_byte_of_byteset, n);
  169. }
  170. BENCHMARK_RELATIVE(FindFirstOf2NeedlesBitSet, n) {
  171. findFirstOfRange(delims2, detail::qfind_first_byte_of_bitset, n);
  172. }
  173. BENCHMARK_DRAW_LINE();
  174. const string delims4 = "bcde";
  175. BENCHMARK(FindFirstOf4NeedlesBase, n) {
  176. findFirstOfRange(delims4, detail::qfind_first_byte_of, n);
  177. }
  178. BENCHMARK_RELATIVE(FindFirstOf4NeedlesNoSSE, n) {
  179. findFirstOfRange(delims4, detail::qfind_first_byte_of_nosse, n);
  180. }
  181. BENCHMARK_RELATIVE(FindFirstOf4NeedlesStd, n) {
  182. findFirstOfRange(delims4, detail::qfind_first_byte_of_std, n);
  183. }
  184. BENCHMARK_RELATIVE(FindFirstOf4NeedlesByteSet, n) {
  185. findFirstOfRange(delims4, detail::qfind_first_byte_of_byteset, n);
  186. }
  187. BENCHMARK_RELATIVE(FindFirstOf4NeedlesBitSet, n) {
  188. findFirstOfRange(delims4, detail::qfind_first_byte_of_bitset, n);
  189. }
  190. BENCHMARK_DRAW_LINE();
  191. const string delims8 = "0123456b";
  192. BENCHMARK(FindFirstOf8NeedlesBase, n) {
  193. findFirstOfRange(delims8, detail::qfind_first_byte_of, n);
  194. }
  195. BENCHMARK_RELATIVE(FindFirstOf8NeedlesNoSSE, n) {
  196. findFirstOfRange(delims8, detail::qfind_first_byte_of_nosse, n);
  197. }
  198. BENCHMARK_RELATIVE(FindFirstOf8NeedlesStd, n) {
  199. findFirstOfRange(delims8, detail::qfind_first_byte_of_std, n);
  200. }
  201. BENCHMARK_RELATIVE(FindFirstOf8NeedlesByteSet, n) {
  202. findFirstOfRange(delims8, detail::qfind_first_byte_of_byteset, n);
  203. }
  204. BENCHMARK_RELATIVE(FindFirstOf8NeedlesBitSet, n) {
  205. findFirstOfRange(delims8, detail::qfind_first_byte_of_bitset, n);
  206. }
  207. BENCHMARK_DRAW_LINE();
  208. const string delims16 = "0123456789bcdefg";
  209. BENCHMARK(FindFirstOf16NeedlesBase, n) {
  210. findFirstOfRange(delims16, detail::qfind_first_byte_of, n);
  211. }
  212. BENCHMARK_RELATIVE(FindFirstOf16NeedlesNoSSE, n) {
  213. findFirstOfRange(delims16, detail::qfind_first_byte_of_nosse, n);
  214. }
  215. BENCHMARK_RELATIVE(FindFirstOf16NeedlesStd, n) {
  216. findFirstOfRange(delims16, detail::qfind_first_byte_of_std, n);
  217. }
  218. BENCHMARK_RELATIVE(FindFirstOf16NeedlesByteSet, n) {
  219. findFirstOfRange(delims16, detail::qfind_first_byte_of_byteset, n);
  220. }
  221. BENCHMARK_RELATIVE(FindFirstOf16NeedlesBitSet, n) {
  222. findFirstOfRange(delims16, detail::qfind_first_byte_of_bitset, n);
  223. }
  224. BENCHMARK_DRAW_LINE();
  225. const string delims32 = "!bcdefghijklmnopqrstuvwxyz_012345";
  226. BENCHMARK(FindFirstOf32NeedlesBase, n) {
  227. findFirstOfRange(delims32, detail::qfind_first_byte_of, n);
  228. }
  229. BENCHMARK_RELATIVE(FindFirstOf32NeedlesNoSSE, n) {
  230. findFirstOfRange(delims32, detail::qfind_first_byte_of_nosse, n);
  231. }
  232. BENCHMARK_RELATIVE(FindFirstOf32NeedlesStd, n) {
  233. findFirstOfRange(delims32, detail::qfind_first_byte_of_std, n);
  234. }
  235. BENCHMARK_RELATIVE(FindFirstOf32NeedlesByteSet, n) {
  236. findFirstOfRange(delims32, detail::qfind_first_byte_of_byteset, n);
  237. }
  238. BENCHMARK_RELATIVE(FindFirstOf32NeedlesBitSet, n) {
  239. findFirstOfRange(delims32, detail::qfind_first_byte_of_bitset, n);
  240. }
  241. BENCHMARK_DRAW_LINE();
  242. const string delims64 =
  243. "!bcdefghijklmnopqrstuvwxyz_"
  244. "ABCDEFGHIJKLMNOPQRSTUVWXYZ-0123456789$";
  245. BENCHMARK(FindFirstOf64NeedlesBase, n) {
  246. findFirstOfRange(delims64, detail::qfind_first_byte_of, n);
  247. }
  248. BENCHMARK_RELATIVE(FindFirstOf64NeedlesNoSSE, n) {
  249. findFirstOfRange(delims64, detail::qfind_first_byte_of_nosse, n);
  250. }
  251. BENCHMARK_RELATIVE(FindFirstOf64NeedlesStd, n) {
  252. findFirstOfRange(delims64, detail::qfind_first_byte_of_std, n);
  253. }
  254. BENCHMARK_RELATIVE(FindFirstOf64NeedlesByteSet, n) {
  255. findFirstOfRange(delims64, detail::qfind_first_byte_of_byteset, n);
  256. }
  257. BENCHMARK_RELATIVE(FindFirstOf64NeedlesBitSet, n) {
  258. findFirstOfRange(delims64, detail::qfind_first_byte_of_bitset, n);
  259. }
  260. BENCHMARK_DRAW_LINE();
  261. template <class Func>
  262. void findFirstOfRandom(Func func, size_t iters) {
  263. for (size_t i = 0; i < iters; ++i) {
  264. auto test = i % ffoDelim.size();
  265. auto p = func(ffoTestString, ffoDelim[test]);
  266. doNotOptimizeAway(p);
  267. }
  268. }
  269. BENCHMARK(FindFirstOfRandomBase, n) {
  270. findFirstOfRandom(detail::qfind_first_byte_of, n);
  271. }
  272. BENCHMARK_RELATIVE(FindFirstOfRandomNoSSE, n) {
  273. findFirstOfRandom(detail::qfind_first_byte_of_nosse, n);
  274. }
  275. BENCHMARK_RELATIVE(FindFirstOfRandomStd, n) {
  276. findFirstOfRandom(detail::qfind_first_byte_of_std, n);
  277. }
  278. BENCHMARK_RELATIVE(FindFirstOfRandomByteSet, n) {
  279. findFirstOfRandom(detail::qfind_first_byte_of_byteset, n);
  280. }
  281. BENCHMARK_RELATIVE(FindFirstOfRandomBitSet, n) {
  282. findFirstOfRandom(detail::qfind_first_byte_of_bitset, n);
  283. }
  284. BENCHMARK_DRAW_LINE();
  285. BENCHMARK(CountDelimsBase, n) {
  286. countHits(detail::qfind_first_byte_of, n);
  287. }
  288. BENCHMARK_RELATIVE(CountDelimsNoSSE, n) {
  289. countHits(detail::qfind_first_byte_of_nosse, n);
  290. }
  291. BENCHMARK_RELATIVE(CountDelimsStd, n) {
  292. countHits(detail::qfind_first_byte_of_std, n);
  293. }
  294. BENCHMARK_RELATIVE(CountDelimsByteSet, n) {
  295. countHits(detail::qfind_first_byte_of_byteset, n);
  296. }
  297. BENCHMARK_RELATIVE(CountDelimsBitSet, n) {
  298. countHits(detail::qfind_first_byte_of_bitset, n);
  299. }
  300. BENCHMARK_DRAW_LINE();
  301. BENCHMARK(FindFirstOfOffsetRange, n) {
  302. StringPiece haystack(str);
  303. folly::StringPiece needles("bc");
  304. DCHECK_EQ(haystack.size() - 1, haystack.find_first_of(needles, 1)); // works!
  305. FOR_EACH_RANGE (i, 0, n) {
  306. size_t pos = i % 2; // not a constant to prevent optimization
  307. doNotOptimizeAway(haystack.find_first_of(needles, pos));
  308. char x = haystack[0];
  309. doNotOptimizeAway(&x);
  310. }
  311. }
  312. BENCHMARK_DRAW_LINE();
  313. int main(int argc, char** argv) {
  314. gflags::ParseCommandLineFlags(&argc, &argv, true);
  315. for (int len : {1, 8, 10, 16, 32, 64, 128, 256, 10 * 1024, 1024 * 1024}) {
  316. initStr(len);
  317. initDelims(len);
  318. initFile(len);
  319. runBenchmarks();
  320. }
  321. return 0;
  322. }