]> git.proxmox.com Git - ceph.git/blame - ceph/src/boost/libs/optional/doc/1A_on_performance.qbk
bump version to 12.2.2-pve1
[ceph.git] / ceph / src / boost / libs / optional / doc / 1A_on_performance.qbk
CommitLineData
7c673cae
FG
1
2[section Performance considerations]
3
4Technical details aside, the memory layout of `optional<T>` is more-less this:
5
6 template <typename T>
7 class optional
8 {
9 bool _initialized;
10 std::aligned_storage_t<sizeof(t), alignof(T)> _storage;
11 };
12
13But for the purpose of this analysis, considering memory layouts, we can think of it as:
14
15 template <typename T>
16 class optional
17 {
18 bool _initialized;
19 T _storage;
20 };
21
22Given type `optional<int>`, and assuming that `sizeof(int) == 4`, we will get `sizeof(optional<int>) == 8`. This is so because of the alignment rules, for our two members we get the following alignment:
23
24[$images/opt_align1.png]
25
26This means you can fit twice as many `int`s as `optional<int>`s into the same space of memory. Therefore, if the size of the objects is critical for your application (e.g., because you want to utilize your CPU cache in order to gain performance) and you have determined you are willing to trade the code clarity, it is recommended that you simply go with type `int` and use some 'magic value' to represent ['not-an-int], or use something like [@https://github.com/akrzemi1/markable `markable`] library.
27
28Even if you cannot spare any value of `int` to represent ['not-an-int] (e.g., because every value is useful, or you do want to signal ['not-an-int] explicitly), at least for `Trivial` types you should consider storing the value and the `bool` flag representing the ['null-state] separately. Consider the following class:
29
30 struct Record
31 {
32 optional<int> _min;
33 optional<int> _max;
34 };
35
36Its memory layout can be depicted as follows:
37
38[$images/opt_align2.png]
39
40This is exactly the same as if we had the following members:
41
42 struct Record
43 {
44 bool _has_min;
45 int _min;
46 bool _has_max;
47 int _max;
48 };
49
50But when they are stored separately, we at least have an option to reorder them like this:
51
52 struct Record
53 {
54 bool _has_min;
55 bool _has_max;
56 int _min;
57 int _max;
58 };
59
60Which gives us the following layout (and smaller total size):
61
62[$images/opt_align3.png]
63
64Sometimes it requires detailed consideration what data we make optional. In our case above, if we determine that both minimum and maximum value can be provided or not provided together, but one is never provided without the other, we can make only one optional memebr:
65
66 struct Limits
67 {
68 int _min;
69 int _max;
70 };
71
72 struct Record
73 {
74 optional<Limits> _limits;
75 };
76
77This would give us the following layout:
78
79[$images/opt_align4.png]
80
81[heading Optional function parameters]
82
83Having function parameters of type `const optional<T>&` may incur certain unexpected run-time cost connected to copy construction of `T`. Consider the following code.
84
85 void fun(const optional<Big>& v)
86 {
87 if (v) doSomethingWith(*v);
88 else doSomethingElse();
89 }
90
91 int main()
92 {
93 optional<Big> ov;
94 Big v;
95 fun(none);
96 fun(ov); // no copy
97 fun(v); // copy constructor of Big
98 }
99
100No copy elision or move semantics can save us from copying type `Big` here. Not that we need any copy, but this is how `optional` works. In order to avoid copying in this case, one could provide second overload of `fun`:
101
102 void fun(const Big& v)
103 {
104 doSomethingWith(v);
105 }
106
107 int main()
108 {
109 optional<Big> ov;
110 Big v;
111 fun(ov); // no copy
112 fun(v); // no copy: second overload selected
113 }
114
115Alternatively, you could consider using an optional reference instead:
116
117 void fun(optional<const Big&> v) // note where the reference is
118 {
119 if (v) doSomethingWith(*v);
120 else doSomethingElse();
121 }
122
123 int main()
124 {
125 optional<Big> ov;
126 Big v;
127 fun(none);
128 fun(ov); // doesn't compile
129 fun(v); // no copy
130 }
131[endsect]