benchmark.cc 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313
  1. // Copyright 2016 Google Inc. All Rights Reserved.
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. // Measures hash function throughput for various input sizes.
  15. #include <algorithm>
  16. #include <cassert>
  17. #include <cstddef>
  18. #include <cstdio>
  19. #include <cstdlib>
  20. #include <map>
  21. #include <string>
  22. #include <utility>
  23. #include <vector>
  24. #include "highwayhash/compiler_specific.h"
  25. #include "highwayhash/instruction_sets.h"
  26. #include "highwayhash/nanobenchmark.h"
  27. #include "highwayhash/os_specific.h"
  28. #include "highwayhash/robust_statistics.h"
  29. // Which functions to enable (includes check for compiler support)
  30. #define BENCHMARK_SIP 0
  31. #define BENCHMARK_SIP_TREE 0
  32. #define BENCHMARK_HIGHWAY 1
  33. #define BENCHMARK_HIGHWAY_CAT 1
  34. #define BENCHMARK_FARM 0
  35. #include "highwayhash/highwayhash_test_target.h"
  36. #if BENCHMARK_SIP
  37. #include "highwayhash/sip_hash.h"
  38. #endif
  39. #if BENCHMARK_SIP_TREE
  40. #include "highwayhash/scalar_sip_tree_hash.h"
  41. #include "highwayhash/sip_tree_hash.h"
  42. #endif
  43. #if BENCHMARK_FARM
  44. #include "third_party/farmhash/src/farmhash.h"
  45. #endif
  46. namespace highwayhash {
  47. namespace {
  48. // Stores time measurements from benchmarks, with support for printing them
  49. // as LaTeX figures or tables.
  50. class Measurements {
  51. public:
  52. void Add(const char* caption, const size_t bytes, const double cycles) {
  53. const float cpb = static_cast<float>(cycles / bytes);
  54. results_.emplace_back(caption, static_cast<int>(bytes), cpb);
  55. }
  56. // Prints results as a LaTeX table (only for in_sizes matching the
  57. // desired values).
  58. void PrintTable(const std::vector<size_t>& in_sizes) {
  59. std::vector<size_t> unique = in_sizes;
  60. std::sort(unique.begin(), unique.end());
  61. unique.erase(std::unique(unique.begin(), unique.end()), unique.end());
  62. printf("\\begin{tabular}{");
  63. for (size_t i = 0; i < unique.size() + 1; ++i) {
  64. printf("%s", i == 0 ? "r" : "|r");
  65. }
  66. printf("}\n\\toprule\nAlgorithm");
  67. for (const size_t in_size : unique) {
  68. printf(" & %zu", in_size);
  69. }
  70. printf("\\\\\n\\midrule\n");
  71. const SpeedsForCaption cpb_for_caption = SortByCaptionFilterBySize(unique);
  72. for (const auto& item : cpb_for_caption) {
  73. printf("%22s", item.first.c_str());
  74. for (const float cpb : item.second) {
  75. printf(" & %5.2f", cpb);
  76. }
  77. printf("\\\\\n");
  78. }
  79. }
  80. // Prints results suitable for pgfplots.
  81. void PrintPlots() {
  82. const SpeedsForCaption cpb_for_caption = SortByCaption();
  83. assert(!cpb_for_caption.empty());
  84. const size_t num_sizes = cpb_for_caption.begin()->second.size();
  85. printf("Size ");
  86. // Flatten per-caption vectors into one iterator.
  87. std::vector<std::vector<float>::const_iterator> iterators;
  88. for (const auto& item : cpb_for_caption) {
  89. printf("%21s ", item.first.c_str());
  90. assert(item.second.size() == num_sizes);
  91. iterators.push_back(item.second.begin());
  92. }
  93. printf("\n");
  94. const std::vector<int>& sizes = UniqueSizes();
  95. assert(num_sizes == sizes.size());
  96. for (int i = 0; i < static_cast<int>(num_sizes); ++i) {
  97. printf("%d ", sizes[i]);
  98. for (auto& it : iterators) {
  99. printf("%5.2f ", 1.0f / *it); // bytes per cycle
  100. ++it;
  101. }
  102. printf("\n");
  103. }
  104. }
  105. private:
  106. struct Result {
  107. Result(const char* caption, const int in_size, const float cpb)
  108. : caption(caption), in_size(in_size), cpb(cpb) {}
  109. // Algorithm name.
  110. std::string caption;
  111. // Size of the input data [bytes].
  112. int in_size;
  113. // Measured throughput [cycles per byte].
  114. float cpb;
  115. };
  116. // Returns set of all input sizes for the first column of a size/speed plot.
  117. std::vector<int> UniqueSizes() {
  118. std::vector<int> sizes;
  119. sizes.reserve(results_.size());
  120. for (const Result& result : results_) {
  121. sizes.push_back(result.in_size);
  122. }
  123. std::sort(sizes.begin(), sizes.end());
  124. sizes.erase(std::unique(sizes.begin(), sizes.end()), sizes.end());
  125. return sizes;
  126. }
  127. using SpeedsForCaption = std::map<std::string, std::vector<float>>;
  128. SpeedsForCaption SortByCaption() const {
  129. SpeedsForCaption cpb_for_caption;
  130. for (const Result& result : results_) {
  131. cpb_for_caption[result.caption].push_back(result.cpb);
  132. }
  133. return cpb_for_caption;
  134. }
  135. // Only includes measurement results matching one of the given sizes.
  136. SpeedsForCaption SortByCaptionFilterBySize(
  137. const std::vector<size_t>& in_sizes) const {
  138. SpeedsForCaption cpb_for_caption;
  139. for (const Result& result : results_) {
  140. for (const size_t in_size : in_sizes) {
  141. if (result.in_size == static_cast<int>(in_size)) {
  142. cpb_for_caption[result.caption].push_back(result.cpb);
  143. }
  144. }
  145. }
  146. return cpb_for_caption;
  147. }
  148. std::vector<Result> results_;
  149. };
  150. void AddMeasurements(DurationsForInputs* input_map, const char* caption,
  151. Measurements* measurements) {
  152. for (size_t i = 0; i < input_map->num_items; ++i) {
  153. const DurationsForInputs::Item& item = input_map->items[i];
  154. std::vector<float> durations(item.durations,
  155. item.durations + item.num_durations);
  156. const float median = Median(&durations);
  157. const float variability = MedianAbsoluteDeviation(durations, median);
  158. printf("%s %4zu: median=%6.1f cycles; median L1 norm =%4.1f cycles\n",
  159. caption, item.input, median, variability);
  160. measurements->Add(caption, item.input, median);
  161. }
  162. input_map->num_items = 0;
  163. }
  164. #if BENCHMARK_SIP || BENCHMARK_FARM || (BENCHMARK_SIP_TREE && defined(__AVX2__))
  165. void MeasureAndAdd(DurationsForInputs* input_map, const char* caption,
  166. const Func func, Measurements* measurements) {
  167. MeasureDurations(func, input_map);
  168. AddMeasurements(input_map, caption, measurements);
  169. }
  170. #endif
  171. // InstructionSets::RunAll callback.
  172. void AddMeasurementsWithPrefix(const char* prefix, const char* target_name,
  173. DurationsForInputs* input_map, void* context) {
  174. std::string caption(prefix);
  175. caption += target_name;
  176. AddMeasurements(input_map, caption.c_str(),
  177. static_cast<Measurements*>(context));
  178. }
  179. #if BENCHMARK_SIP
  180. uint64_t RunSip(const size_t size) {
  181. const HH_U64 key2[2] HH_ALIGNAS(16) = {0, 1};
  182. char in[kMaxBenchmarkInputSize];
  183. memcpy(in, &size, sizeof(size));
  184. return SipHash(key2, in, size);
  185. }
  186. uint64_t RunSip13(const size_t size) {
  187. const HH_U64 key2[2] HH_ALIGNAS(16) = {0, 1};
  188. char in[kMaxBenchmarkInputSize];
  189. memcpy(in, &size, sizeof(size));
  190. return SipHash13(key2, in, size);
  191. }
  192. #endif
  193. #if BENCHMARK_SIP_TREE
  194. uint64_t RunSipTree(const size_t size) {
  195. const HH_U64 key4[4] HH_ALIGNAS(32) = {0, 1, 2, 3};
  196. char in[kMaxBenchmarkInputSize];
  197. memcpy(in, &size, sizeof(size));
  198. return SipTreeHash(key4, in, size);
  199. }
  200. uint64_t RunSipTree13(const size_t size) {
  201. const HH_U64 key4[4] HH_ALIGNAS(32) = {0, 1, 2, 3};
  202. char in[kMaxBenchmarkInputSize];
  203. memcpy(in, &size, sizeof(size));
  204. return SipTreeHash13(key4, in, size);
  205. }
  206. #endif
  207. #if BENCHMARK_FARM
  208. uint64_t RunFarm(const size_t size) {
  209. char in[kMaxBenchmarkInputSize];
  210. memcpy(in, &size, sizeof(size));
  211. return farmhash::Fingerprint64(reinterpret_cast<const char*>(in), size);
  212. }
  213. #endif
  214. void AddMeasurements(const std::vector<size_t>& in_sizes,
  215. Measurements* measurements) {
  216. DurationsForInputs input_map(in_sizes.data(), in_sizes.size(), 40);
  217. #if BENCHMARK_SIP
  218. MeasureAndAdd(&input_map, "SipHash", RunSip, measurements);
  219. MeasureAndAdd(&input_map, "SipHash13", RunSip13, measurements);
  220. #endif
  221. #if BENCHMARK_SIP_TREE && defined(__AVX2__)
  222. MeasureAndAdd(&input_map, "SipTreeHash", RunSipTree, measurements);
  223. MeasureAndAdd(&input_map, "SipTreeHash13", RunSipTree13, measurements);
  224. #endif
  225. #if BENCHMARK_FARM
  226. MeasureAndAdd(&input_map, "Farm", &RunFarm, measurements);
  227. #endif
  228. #if BENCHMARK_HIGHWAY
  229. InstructionSets::RunAll<HighwayHashBenchmark>(
  230. &input_map, &AddMeasurementsWithPrefix, measurements);
  231. #endif
  232. #if BENCHMARK_HIGHWAY_CAT
  233. InstructionSets::RunAll<HighwayHashCatBenchmark>(
  234. &input_map, &AddMeasurementsWithPrefix, measurements);
  235. #endif
  236. }
  237. void PrintTable() {
  238. const std::vector<size_t> in_sizes = {
  239. 7, 8, 31, 32, 63, 64, kMaxBenchmarkInputSize};
  240. Measurements measurements;
  241. AddMeasurements(in_sizes, &measurements);
  242. measurements.PrintTable(in_sizes);
  243. }
  244. void PrintPlots() {
  245. std::vector<size_t> in_sizes;
  246. for (int num_vectors = 0; num_vectors < 12; ++num_vectors) {
  247. for (int remainder : {0, 9, 18, 27}) {
  248. in_sizes.push_back(num_vectors * 32 + remainder);
  249. assert(in_sizes.back() <= kMaxBenchmarkInputSize);
  250. }
  251. }
  252. Measurements measurements;
  253. AddMeasurements(in_sizes, &measurements);
  254. measurements.PrintPlots();
  255. }
  256. } // namespace
  257. } // namespace highwayhash
  258. int main(int argc, char* argv[]) {
  259. highwayhash::PinThreadToRandomCPU();
  260. // No argument or t => table
  261. if (argc < 2 || argv[1][0] == 't') {
  262. highwayhash::PrintTable();
  263. } else if (argv[1][0] == 'p') {
  264. highwayhash::PrintPlots();
  265. }
  266. return 0;
  267. }