Imported Upstream version 1.72.0
[platform/upstream/boost.git] / libs / histogram / doc / benchmarks.qbk
1 [/
2             Copyright Hans Dembinski 2018 - 2019.
3    Distributed under the Boost Software License, Version 1.0.
4       (See accompanying file LICENSE_1_0.txt or copy at
5             https://www.boost.org/LICENSE_1_0.txt)
6 ]
7
8 [section:benchmarks Benchmarks]
9
10 The library is designed to be fast. When configured correctly, it is one of the fastest libraries on the market. If you find a library that is faster than Boost.Histogram, please submit an issue on Github. We care about performance.
11
12 That being said, the time spend in filling the histogram is usually not the bottleneck of an application. Only in processing of really large data sets the performance of the histogram can be important.
13
14 All benchmarks are compiled on a laptop with a 2,9 GHz Intel Core i5 processor with Apple LLVM (clang-1001.0.46.4) and the flags `-DNDEBUG -O3 -funsafe-math-optimizations`. Adding `-fno-exceptions -fno-rtti` would increase the Boost.Histogram performance by another (10-20) %, but this is not done here out of fairness, since the ROOT histograms do not compile with these options.
15
16 [section:fill_performance Fill performance]
17
18 The fill performance of different configurations of Boost.Histogram are compared with histogram classes and functions from other libraries. Random numbers from a uniform and a normal distribution are filled into histograms with 1, 2, 3, and 6 axes. 100 bins per axis are used for 1, 2, 3 axes. 10 bins per axis for the case with 6 axes. The histogram can be filled with the call operator `operator()` or the more efficient `fill`-method. Results are shown for both. The GSL offers only 1D and 2D histograms, so there are no entries for the higher dimensional benchmarks. Raw timing results are converted to average number of CPU cycles used per input value.
19
20 There is one bar for each benchmark, and the upper end has a hatched part. The full bar is the result when the histograms are filled with random normally distributed data that falls outside of the axis domain in about 10 % of the cases. This makes the branch predictors in the CPU fail every now and then, which degrades performance. The bar without the hatched part is the result when the histograms are filled with uniform random numbers which are always inside the axis range.
21
22 [$../fill_performance.svg]
23
24 [variablelist
25         [[ROOT 6] [[@https://root.cern.ch ROOT classes] (`TH1I` for 1D, `TH2I` for 2D, `TH3I` for 3D and `THnI` for 6D)]]
26
27         [[GSL] [[@https://www.gnu.org/software/gsl/doc/html/histogram.html GSL histograms] for 1D and 2D]]
28
29         [[boost-SS] [Histogram with `std::tuple<axis::regular<>>` and `std::vector<int>`]]
30
31         [[boost-SD] [Histogram with `std::tuple<axis::regular<>>` with [classref boost::histogram::unlimited_storage]]]
32
33         [[boost-DS] [Histogram with `std::vector<axis::variant<axis::regular<>>>` with `std::vector<int>`]]
34
35         [[boost-DD] [Histogram with `std::vector<axis::variant<axis::regular<>>>` with [classref boost::histogram::unlimited_storage]]]
36 ]
37
38 Boost.Histogram is faster than other libraries. Simultaneously, it is much more flexible, since the axis and storage types can be customized.
39
40 When `operator()` is used, a histogram with compile-time configured axes is always faster than one with run-time configured axes. The [classref boost::histogram::unlimited_storage] is faster than a `std::vector<int>` for histograms with many bins, because it uses the cache more effectively due to its smaller memory consumption per bin. If the number of bins is small, it is slower because of the overhead of handling memory dynamically. If the `fill` method is used, histograms with run-time configured axes are as fast for 2D histograms and higher. In this case, using `std::vector<int>` for storage is faster in all benchmarks that were carried out, although the performance gap to [classref boost::histogram::unlimited_storage] shrinks for higher dimensions.
41
42 [endsect]
43
44 [section:iteration_performance Iteration performance]
45
46 Boost.Histogram provides the [funcref boost::histogram::indexed] range generator for convenient iteration over the histogram cells. Using the range generator is very convenient and it is faster than by writing nested for-loops.
47 ```
48 // nested for loops over 2d histogram
49 for (int i = 0; i < h.axis(0).size(); ++i) {
50   for (int j = 0; j < h.axis(1).size(); ++j) {
51     std::cout << i << " " << j << " " << h.at(i, j) << std::endl;
52   }
53 }
54
55 // same, with indexed range generator
56 for (auto&& x : boost::histogram::indexed(h)) {
57   std::cout << x.index(0) << " " << x.index(1) << " " << *x << std::endl;
58 }
59 ```
60
61 The access time per bin is compared for these two iteration strategies for histograms that hold the axes in a `std::tuple` (tuple), in a `std::vector` (vector), and in a `std::vector<boost::histogram::axis::variant>` (vector of variants). The access time per bin is measured for axis with 4 to 128 bins per axis.
62
63 [$../iteration_performance.svg]
64
65 [endsect]
66
67 [endsect]