Skip to content

Commit 0d2aa0e

Browse files
committed
[hist] Document design decisions and implementation choices
1 parent b172ac6 commit 0d2aa0e

1 file changed

Lines changed: 73 additions & 0 deletions

File tree

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
# Design and Implementation
2+
3+
This document describes key design decisions and implementation choices.
4+
5+
## Templating
6+
7+
Classes are only templated if required for data members, in particular the bin content type `T`.
8+
We use member function templates to accept variable number of arguments (see also below).
9+
Classes are **not** templated to improve performance, in particular not on the axis type(s).
10+
This avoids an explosion of types and simplifies serialization.
11+
Instead axis objects are run-time choices and stored in a `std::variant`.
12+
With a careful design, this still results in excellent performance.
13+
14+
## Performance Optimizations
15+
16+
If required, it would be possible to template performance-critical functions on the axis types.
17+
This was shown beneficial in microbenchmarks for one-dimensional histograms.
18+
However, it will not be implemented until shown useful in a real-world application.
19+
In virtually all cases, filling a (one-dimensional) histogram is negligible compared to reading, decompressing, and processing of data.
20+
21+
The same applies for other optimizations, such as caching the pointer to the axis object stored in the `std::variant`.
22+
Such optimizations should only be implemented with a careful motivation for real-world applications.
23+
24+
## Functions with Variable Number of Arguments
25+
26+
Many member functions have two overloads: one accepting a function parameter pack and one accepting a `std::tuple` or `std::array`.
27+
28+
### Arguments with Different Types
29+
30+
Functions that take arguments with different types expect a `std::tuple`.
31+
An example is `template <typename A...> void Fill(const std::tuple<A...> &args)`.
32+
33+
For user-convenience, a variadic function template forwards to the `std::tuple` overload:
34+
```cpp
35+
template <typename... A> void Fill(const A &...args) {
36+
Fill(std::forward_as_tuple(args...));
37+
}
38+
```
39+
This will forward the arguments as references, so no copy-constructors are called (that could potentially be expensive).
40+
41+
### Arguments with Same Type
42+
43+
In this case, the function has a `std::size_t N` template argument and accepts a `std::array`.
44+
An example is `template <std::size_t N> const T &GetBinContent(const std::array<RBinIndex, N> &args)`
45+
46+
For user-convenience, a variadic function template forwards to the `std::array` overload:
47+
```cpp
48+
template <typename... A> const T &GetBinContent(const A &...args) {
49+
std::array<RBinIndex, sizeof...(A)> a{args...};
50+
return GetBinContent(a);
51+
}
52+
```
53+
This will copy the arguments, which is fine in this case because `RBinIndex` is small (see below).
54+
55+
### Special Arguments
56+
57+
Special arguments are passed last.
58+
Examples include
59+
```cpp
60+
template <typename... A> void Fill(const std::tuple<A...> &args, RWeight w);
61+
template <std::size_t N> void SetBinContent(const std::array<RBinIndex, N> &args, const T &content);
62+
```
63+
The same works for the variadic function templates that will check the type of the last argument.
64+
65+
For profiles, we accept the value with a template type as well to allow automatic conversion to `double`, for example from `int`.
66+
67+
## Miscellaneous
68+
69+
The implementation uses standard [C++17](https://en.cppreference.com/w/cpp/17.html):
70+
* No backports from later C++ versions, such as `std::span`, and
71+
* No ROOT types, to make sure the histogram package can be compiled standalone.
72+
73+
Small objects are passed by value instead of by reference (`RBinIndex`, `RWeight`).

0 commit comments

Comments
 (0)