/root/bitcoin/src/cluster_linearize.h
Line | Count | Source |
1 | | // Copyright (c) The Bitcoin Core developers |
2 | | // Distributed under the MIT software license, see the accompanying |
3 | | // file COPYING or http://www.opensource.org/licenses/mit-license.php. |
4 | | |
5 | | #ifndef BITCOIN_CLUSTER_LINEARIZE_H |
6 | | #define BITCOIN_CLUSTER_LINEARIZE_H |
7 | | |
8 | | #include <algorithm> |
9 | | #include <cstdint> |
10 | | #include <numeric> |
11 | | #include <optional> |
12 | | #include <ranges> |
13 | | #include <utility> |
14 | | #include <vector> |
15 | | |
16 | | #include <attributes.h> |
17 | | #include <memusage.h> |
18 | | #include <random.h> |
19 | | #include <span.h> |
20 | | #include <util/feefrac.h> |
21 | | #include <util/vecdeque.h> |
22 | | |
23 | | namespace cluster_linearize { |
24 | | |
25 | | /** Data type to represent transaction indices in DepGraphs and the clusters they represent. */ |
26 | | using DepGraphIndex = uint32_t; |
27 | | |
28 | | /** Data structure that holds a transaction graph's preprocessed data (fee, size, ancestors, |
29 | | * descendants). */ |
30 | | template<typename SetType> |
31 | | class DepGraph |
32 | | { |
33 | | /** Information about a single transaction. */ |
34 | | struct Entry |
35 | | { |
36 | | /** Fee and size of transaction itself. */ |
37 | | FeeFrac feerate; |
38 | | /** All ancestors of the transaction (including itself). */ |
39 | | SetType ancestors; |
40 | | /** All descendants of the transaction (including itself). */ |
41 | | SetType descendants; |
42 | | |
43 | | /** Equality operator (primarily for testing purposes). */ |
44 | 296k | friend bool operator==(const Entry&, const Entry&) noexcept = default; Branch (44:77): [True: 59.2k, False: 0]
Branch (44:77): [True: 59.2k, False: 0]
Branch (44:77): [True: 59.2k, False: 0]
|
45 | | |
46 | | /** Construct an empty entry. */ |
47 | 72.5k | Entry() noexcept = default; |
48 | | /** Construct an entry with a given feerate, ancestor set, descendant set. */ |
49 | 2.50M | Entry(const FeeFrac& f, const SetType& a, const SetType& d) noexcept : feerate(f), ancestors(a), descendants(d) {}_ZN17cluster_linearize8DepGraphIN13bitset_detail9IntBitSetIjEEE5EntryC2ERK7FeeFracRKS3_SA_ Line | Count | Source | 49 | 70.0k | Entry(const FeeFrac& f, const SetType& a, const SetType& d) noexcept : feerate(f), ancestors(a), descendants(d) {} |
_ZN17cluster_linearize8DepGraphIN13bitset_detail14MultiIntBitSetImLj2EEEE5EntryC2ERK7FeeFracRKS3_SA_ Line | Count | Source | 49 | 164k | Entry(const FeeFrac& f, const SetType& a, const SetType& d) noexcept : feerate(f), ancestors(a), descendants(d) {} |
_ZN17cluster_linearize8DepGraphIN13bitset_detail9IntBitSetImEEE5EntryC2ERK7FeeFracRKS3_SA_ Line | Count | Source | 49 | 2.27M | Entry(const FeeFrac& f, const SetType& a, const SetType& d) noexcept : feerate(f), ancestors(a), descendants(d) {} |
|
50 | | }; |
51 | | |
52 | | /** Data for each transaction. */ |
53 | | std::vector<Entry> entries; |
54 | | |
55 | | /** Which positions are used. */ |
56 | | SetType m_used; |
57 | | |
58 | | public: |
59 | | /** Equality operator (primarily for testing purposes). */ |
60 | | friend bool operator==(const DepGraph& a, const DepGraph& b) noexcept |
61 | 880 | { |
62 | 880 | if (a.m_used != b.m_used) return false; Branch (62:13): [True: 0, False: 880]
|
63 | | // Only compare the used positions within the entries vector. |
64 | 14.8k | for (auto idx : a.m_used) { Branch (64:23): [True: 14.8k, False: 880]
|
65 | 14.8k | if (a.entries[idx] != b.entries[idx]) return false; Branch (65:17): [True: 0, False: 14.8k]
|
66 | 14.8k | } |
67 | 880 | return true; |
68 | 880 | } |
69 | | |
70 | | // Default constructors. |
71 | 1.66M | DepGraph() noexcept = default; _ZN17cluster_linearize8DepGraphIN13bitset_detail9IntBitSetIjEEEC2Ev Line | Count | Source | 71 | 5.89k | DepGraph() noexcept = default; |
_ZN17cluster_linearize8DepGraphIN13bitset_detail14MultiIntBitSetImLj2EEEEC2Ev Line | Count | Source | 71 | 2.58k | DepGraph() noexcept = default; |
_ZN17cluster_linearize8DepGraphIN13bitset_detail9IntBitSetImEEEC2Ev Line | Count | Source | 71 | 1.65M | DepGraph() noexcept = default; |
|
72 | 12.3k | DepGraph(const DepGraph&) noexcept = default; |
73 | 0 | DepGraph(DepGraph&&) noexcept = default; |
74 | 496k | DepGraph& operator=(const DepGraph&) noexcept = default; |
75 | 539k | DepGraph& operator=(DepGraph&&) noexcept = default; _ZN17cluster_linearize8DepGraphIN13bitset_detail9IntBitSetIjEEEaSEOS4_ Line | Count | Source | 75 | 3.16k | DepGraph& operator=(DepGraph&&) noexcept = default; |
_ZN17cluster_linearize8DepGraphIN13bitset_detail14MultiIntBitSetImLj2EEEEaSEOS4_ Line | Count | Source | 75 | 2.84k | DepGraph& operator=(DepGraph&&) noexcept = default; |
_ZN17cluster_linearize8DepGraphIN13bitset_detail9IntBitSetImEEEaSEOS4_ Line | Count | Source | 75 | 533k | DepGraph& operator=(DepGraph&&) noexcept = default; |
|
76 | | |
77 | | /** Construct a DepGraph object given another DepGraph and a mapping from old to new. |
78 | | * |
79 | | * @param depgraph The original DepGraph that is being remapped. |
80 | | * |
81 | | * @param mapping A span such that mapping[i] gives the position in the new DepGraph |
82 | | * for position i in the old depgraph. Its size must be equal to |
83 | | * depgraph.PositionRange(). The value of mapping[i] is ignored if |
84 | | * position i is a hole in depgraph (i.e., if !depgraph.Positions()[i]). |
85 | | * |
86 | | * @param pos_range The PositionRange() for the new DepGraph. It must equal the largest |
87 | | * value in mapping for any used position in depgraph plus 1, or 0 if |
88 | | * depgraph.TxCount() == 0. |
89 | | * |
90 | | * Complexity: O(N^2) where N=depgraph.TxCount(). |
91 | | */ |
92 | 2.72k | DepGraph(const DepGraph<SetType>& depgraph, std::span<const DepGraphIndex> mapping, DepGraphIndex pos_range) noexcept : entries(pos_range) |
93 | 2.72k | { |
94 | 2.72k | Assume(mapping.size() == depgraph.PositionRange()); |
95 | 2.72k | Assume((pos_range == 0) == (depgraph.TxCount() == 0)); |
96 | 47.7k | for (DepGraphIndex i : depgraph.Positions()) { Branch (96:30): [True: 47.7k, False: 2.72k]
|
97 | 47.7k | auto new_idx = mapping[i]; |
98 | 47.7k | Assume(new_idx < pos_range); |
99 | | // Add transaction. |
100 | 47.7k | entries[new_idx].ancestors = SetType::Singleton(new_idx); |
101 | 47.7k | entries[new_idx].descendants = SetType::Singleton(new_idx); |
102 | 47.7k | m_used.Set(new_idx); |
103 | | // Fill in fee and size. |
104 | 47.7k | entries[new_idx].feerate = depgraph.entries[i].feerate; |
105 | 47.7k | } |
106 | 47.7k | for (DepGraphIndex i : depgraph.Positions()) { Branch (106:30): [True: 47.7k, False: 2.72k]
|
107 | | // Fill in dependencies by mapping direct parents. |
108 | 47.7k | SetType parents; |
109 | 47.7k | for (auto j : depgraph.GetReducedParents(i)) parents.Set(mapping[j]); Branch (109:25): [True: 38.6k, False: 47.7k]
|
110 | 47.7k | AddDependencies(parents, mapping[i]); |
111 | 47.7k | } |
112 | | // Verify that the provided pos_range was correct (no unused positions at the end). |
113 | 2.72k | Assume(m_used.None() ? (pos_range == 0) : (pos_range == m_used.Last() + 1)); |
114 | 2.72k | } |
115 | | |
116 | | /** Get the set of transactions positions in use. Complexity: O(1). */ |
117 | 10.6M | const SetType& Positions() const noexcept { return m_used; }_ZNK17cluster_linearize8DepGraphIN13bitset_detail9IntBitSetIjEEE9PositionsEv Line | Count | Source | 117 | 155k | const SetType& Positions() const noexcept { return m_used; } |
_ZNK17cluster_linearize8DepGraphIN13bitset_detail14MultiIntBitSetImLj2EEEE9PositionsEv Line | Count | Source | 117 | 7.64M | const SetType& Positions() const noexcept { return m_used; } |
_ZNK17cluster_linearize8DepGraphIN13bitset_detail9IntBitSetImEEE9PositionsEv Line | Count | Source | 117 | 2.82M | const SetType& Positions() const noexcept { return m_used; } |
|
118 | | /** Get the range of positions in this DepGraph. All entries in Positions() are in [0, PositionRange() - 1]. */ |
119 | 3.77M | DepGraphIndex PositionRange() const noexcept { return entries.size(); }_ZNK17cluster_linearize8DepGraphIN13bitset_detail9IntBitSetIjEEE13PositionRangeEv Line | Count | Source | 119 | 27.3k | DepGraphIndex PositionRange() const noexcept { return entries.size(); } |
_ZNK17cluster_linearize8DepGraphIN13bitset_detail14MultiIntBitSetImLj2EEEE13PositionRangeEv Line | Count | Source | 119 | 1.16k | DepGraphIndex PositionRange() const noexcept { return entries.size(); } |
_ZNK17cluster_linearize8DepGraphIN13bitset_detail9IntBitSetImEEE13PositionRangeEv Line | Count | Source | 119 | 3.74M | DepGraphIndex PositionRange() const noexcept { return entries.size(); } |
|
120 | | /** Get the number of transactions in the graph. Complexity: O(1). */ |
121 | 1.79M | auto TxCount() const noexcept { return m_used.Count(); }_ZNK17cluster_linearize8DepGraphIN13bitset_detail9IntBitSetIjEEE7TxCountEv Line | Count | Source | 121 | 56.2k | auto TxCount() const noexcept { return m_used.Count(); } |
_ZNK17cluster_linearize8DepGraphIN13bitset_detail14MultiIntBitSetImLj2EEEE7TxCountEv Line | Count | Source | 121 | 1.60M | auto TxCount() const noexcept { return m_used.Count(); } |
_ZNK17cluster_linearize8DepGraphIN13bitset_detail9IntBitSetImEEE7TxCountEv Line | Count | Source | 121 | 137k | auto TxCount() const noexcept { return m_used.Count(); } |
|
122 | | /** Get the feerate of a given transaction i. Complexity: O(1). */ |
123 | 47.3M | const FeeFrac& FeeRate(DepGraphIndex i) const noexcept { return entries[i].feerate; }_ZNK17cluster_linearize8DepGraphIN13bitset_detail9IntBitSetIjEEE7FeeRateEj Line | Count | Source | 123 | 3.44M | const FeeFrac& FeeRate(DepGraphIndex i) const noexcept { return entries[i].feerate; } |
_ZNK17cluster_linearize8DepGraphIN13bitset_detail14MultiIntBitSetImLj2EEEE7FeeRateEj Line | Count | Source | 123 | 3.27M | const FeeFrac& FeeRate(DepGraphIndex i) const noexcept { return entries[i].feerate; } |
_ZNK17cluster_linearize8DepGraphIN13bitset_detail9IntBitSetImEEE7FeeRateEj Line | Count | Source | 123 | 40.5M | const FeeFrac& FeeRate(DepGraphIndex i) const noexcept { return entries[i].feerate; } |
|
124 | | /** Get the mutable feerate of a given transaction i. Complexity: O(1). */ |
125 | 11.6M | FeeFrac& FeeRate(DepGraphIndex i) noexcept { return entries[i].feerate; }_ZN17cluster_linearize8DepGraphIN13bitset_detail9IntBitSetIjEEE7FeeRateEj Line | Count | Source | 125 | 63.6k | FeeFrac& FeeRate(DepGraphIndex i) noexcept { return entries[i].feerate; } |
_ZN17cluster_linearize8DepGraphIN13bitset_detail14MultiIntBitSetImLj2EEEE7FeeRateEj Line | Count | Source | 125 | 9.61M | FeeFrac& FeeRate(DepGraphIndex i) noexcept { return entries[i].feerate; } |
_ZN17cluster_linearize8DepGraphIN13bitset_detail9IntBitSetImEEE7FeeRateEj Line | Count | Source | 125 | 1.93M | FeeFrac& FeeRate(DepGraphIndex i) noexcept { return entries[i].feerate; } |
|
126 | | /** Get the ancestors of a given transaction i. Complexity: O(1). */ |
127 | 133M | const SetType& Ancestors(DepGraphIndex i) const noexcept { return entries[i].ancestors; }_ZNK17cluster_linearize8DepGraphIN13bitset_detail9IntBitSetIjEEE9AncestorsEj Line | Count | Source | 127 | 59.4M | const SetType& Ancestors(DepGraphIndex i) const noexcept { return entries[i].ancestors; } |
_ZNK17cluster_linearize8DepGraphIN13bitset_detail14MultiIntBitSetImLj2EEEE9AncestorsEj Line | Count | Source | 127 | 27.4M | const SetType& Ancestors(DepGraphIndex i) const noexcept { return entries[i].ancestors; } |
_ZNK17cluster_linearize8DepGraphIN13bitset_detail9IntBitSetImEEE9AncestorsEj Line | Count | Source | 127 | 46.4M | const SetType& Ancestors(DepGraphIndex i) const noexcept { return entries[i].ancestors; } |
|
128 | | /** Get the descendants of a given transaction i. Complexity: O(1). */ |
129 | 57.6M | const SetType& Descendants(DepGraphIndex i) const noexcept { return entries[i].descendants; }_ZNK17cluster_linearize8DepGraphIN13bitset_detail9IntBitSetIjEEE11DescendantsEj Line | Count | Source | 129 | 18.4M | const SetType& Descendants(DepGraphIndex i) const noexcept { return entries[i].descendants; } |
_ZNK17cluster_linearize8DepGraphIN13bitset_detail14MultiIntBitSetImLj2EEEE11DescendantsEj Line | Count | Source | 129 | 25.6M | const SetType& Descendants(DepGraphIndex i) const noexcept { return entries[i].descendants; } |
_ZNK17cluster_linearize8DepGraphIN13bitset_detail9IntBitSetImEEE11DescendantsEj Line | Count | Source | 129 | 13.5M | const SetType& Descendants(DepGraphIndex i) const noexcept { return entries[i].descendants; } |
|
130 | | |
131 | | /** Add a new unconnected transaction to this transaction graph (in the first available |
132 | | * position), and return its DepGraphIndex. |
133 | | * |
134 | | * Complexity: O(1) (amortized, due to resizing of backing vector). |
135 | | */ |
136 | | DepGraphIndex AddTransaction(const FeeFrac& feefrac) noexcept |
137 | 2.50M | { |
138 | 2.50M | static constexpr auto ALL_POSITIONS = SetType::Fill(SetType::Size()); |
139 | 2.50M | auto available = ALL_POSITIONS - m_used; |
140 | 2.50M | Assume(available.Any()); |
141 | 2.50M | DepGraphIndex new_idx = available.First(); |
142 | 2.50M | if (new_idx == entries.size()) { Branch (142:13): [True: 61.5k, False: 8.48k]
Branch (142:13): [True: 149k, False: 15.3k]
Branch (142:13): [True: 2.27M, False: 0]
|
143 | 2.48M | entries.emplace_back(feefrac, SetType::Singleton(new_idx), SetType::Singleton(new_idx)); |
144 | 2.48M | } else { |
145 | 23.8k | entries[new_idx] = Entry(feefrac, SetType::Singleton(new_idx), SetType::Singleton(new_idx)); |
146 | 23.8k | } |
147 | 2.50M | m_used.Set(new_idx); |
148 | 2.50M | return new_idx; |
149 | 2.50M | } _ZN17cluster_linearize8DepGraphIN13bitset_detail9IntBitSetIjEEE14AddTransactionERK7FeeFrac Line | Count | Source | 137 | 70.0k | { | 138 | 70.0k | static constexpr auto ALL_POSITIONS = SetType::Fill(SetType::Size()); | 139 | 70.0k | auto available = ALL_POSITIONS - m_used; | 140 | 70.0k | Assume(available.Any()); | 141 | 70.0k | DepGraphIndex new_idx = available.First(); | 142 | 70.0k | if (new_idx == entries.size()) { Branch (142:13): [True: 61.5k, False: 8.48k]
| 143 | 61.5k | entries.emplace_back(feefrac, SetType::Singleton(new_idx), SetType::Singleton(new_idx)); | 144 | 61.5k | } else { | 145 | 8.48k | entries[new_idx] = Entry(feefrac, SetType::Singleton(new_idx), SetType::Singleton(new_idx)); | 146 | 8.48k | } | 147 | 70.0k | m_used.Set(new_idx); | 148 | 70.0k | return new_idx; | 149 | 70.0k | } |
_ZN17cluster_linearize8DepGraphIN13bitset_detail14MultiIntBitSetImLj2EEEE14AddTransactionERK7FeeFrac Line | Count | Source | 137 | 164k | { | 138 | 164k | static constexpr auto ALL_POSITIONS = SetType::Fill(SetType::Size()); | 139 | 164k | auto available = ALL_POSITIONS - m_used; | 140 | 164k | Assume(available.Any()); | 141 | 164k | DepGraphIndex new_idx = available.First(); | 142 | 164k | if (new_idx == entries.size()) { Branch (142:13): [True: 149k, False: 15.3k]
| 143 | 149k | entries.emplace_back(feefrac, SetType::Singleton(new_idx), SetType::Singleton(new_idx)); | 144 | 149k | } else { | 145 | 15.3k | entries[new_idx] = Entry(feefrac, SetType::Singleton(new_idx), SetType::Singleton(new_idx)); | 146 | 15.3k | } | 147 | 164k | m_used.Set(new_idx); | 148 | 164k | return new_idx; | 149 | 164k | } |
_ZN17cluster_linearize8DepGraphIN13bitset_detail9IntBitSetImEEE14AddTransactionERK7FeeFrac Line | Count | Source | 137 | 2.27M | { | 138 | 2.27M | static constexpr auto ALL_POSITIONS = SetType::Fill(SetType::Size()); | 139 | 2.27M | auto available = ALL_POSITIONS - m_used; | 140 | 2.27M | Assume(available.Any()); | 141 | 2.27M | DepGraphIndex new_idx = available.First(); | 142 | 2.27M | if (new_idx == entries.size()) { Branch (142:13): [True: 2.27M, False: 0]
| 143 | 2.27M | entries.emplace_back(feefrac, SetType::Singleton(new_idx), SetType::Singleton(new_idx)); | 144 | 2.27M | } else { | 145 | 0 | entries[new_idx] = Entry(feefrac, SetType::Singleton(new_idx), SetType::Singleton(new_idx)); | 146 | 0 | } | 147 | 2.27M | m_used.Set(new_idx); | 148 | 2.27M | return new_idx; | 149 | 2.27M | } |
|
150 | | |
151 | | /** Remove the specified positions from this DepGraph. |
152 | | * |
153 | | * The specified positions will no longer be part of Positions(), and dependencies with them are |
154 | | * removed. Note that due to DepGraph only tracking ancestors/descendants (and not direct |
155 | | * dependencies), if a parent is removed while a grandparent remains, the grandparent will |
156 | | * remain an ancestor. |
157 | | * |
158 | | * Complexity: O(N) where N=TxCount(). |
159 | | */ |
160 | | void RemoveTransactions(const SetType& del) noexcept |
161 | 675k | { |
162 | 675k | m_used -= del; |
163 | | // Remove now-unused trailing entries. |
164 | 1.63M | while (!entries.empty() && !m_used[entries.size() - 1]) { Branch (164:16): [True: 9.63k, False: 516]
Branch (164:36): [True: 4.17k, False: 5.46k]
Branch (164:16): [True: 188k, False: 772]
Branch (164:36): [True: 29.5k, False: 159k]
Branch (164:16): [True: 1.05M, False: 377k]
Branch (164:36): [True: 922k, False: 132k]
|
165 | 956k | entries.pop_back(); |
166 | 956k | } |
167 | | // Remove the deleted transactions from ancestors/descendants of other transactions. Note |
168 | | // that the deleted positions will retain old feerate and dependency information. This does |
169 | | // not matter as they will be overwritten by AddTransaction if they get used again. |
170 | 13.0M | for (auto& entry : entries) { Branch (170:26): [True: 89.6k, False: 5.97k]
Branch (170:26): [True: 11.9M, False: 159k]
Branch (170:26): [True: 1.04M, False: 510k]
|
171 | 13.0M | entry.ancestors &= m_used; |
172 | 13.0M | entry.descendants &= m_used; |
173 | 13.0M | } |
174 | 675k | } _ZN17cluster_linearize8DepGraphIN13bitset_detail9IntBitSetIjEEE18RemoveTransactionsERKS3_ Line | Count | Source | 161 | 5.97k | { | 162 | 5.97k | m_used -= del; | 163 | | // Remove now-unused trailing entries. | 164 | 10.1k | while (!entries.empty() && !m_used[entries.size() - 1]) { Branch (164:16): [True: 9.63k, False: 516]
Branch (164:36): [True: 4.17k, False: 5.46k]
| 165 | 4.17k | entries.pop_back(); | 166 | 4.17k | } | 167 | | // Remove the deleted transactions from ancestors/descendants of other transactions. Note | 168 | | // that the deleted positions will retain old feerate and dependency information. This does | 169 | | // not matter as they will be overwritten by AddTransaction if they get used again. | 170 | 89.6k | for (auto& entry : entries) { Branch (170:26): [True: 89.6k, False: 5.97k]
| 171 | 89.6k | entry.ancestors &= m_used; | 172 | 89.6k | entry.descendants &= m_used; | 173 | 89.6k | } | 174 | 5.97k | } |
_ZN17cluster_linearize8DepGraphIN13bitset_detail14MultiIntBitSetImLj2EEEE18RemoveTransactionsERKS3_ Line | Count | Source | 161 | 159k | { | 162 | 159k | m_used -= del; | 163 | | // Remove now-unused trailing entries. | 164 | 189k | while (!entries.empty() && !m_used[entries.size() - 1]) { Branch (164:16): [True: 188k, False: 772]
Branch (164:36): [True: 29.5k, False: 159k]
| 165 | 29.5k | entries.pop_back(); | 166 | 29.5k | } | 167 | | // Remove the deleted transactions from ancestors/descendants of other transactions. Note | 168 | | // that the deleted positions will retain old feerate and dependency information. This does | 169 | | // not matter as they will be overwritten by AddTransaction if they get used again. | 170 | 11.9M | for (auto& entry : entries) { Branch (170:26): [True: 11.9M, False: 159k]
| 171 | 11.9M | entry.ancestors &= m_used; | 172 | 11.9M | entry.descendants &= m_used; | 173 | 11.9M | } | 174 | 159k | } |
_ZN17cluster_linearize8DepGraphIN13bitset_detail9IntBitSetImEEE18RemoveTransactionsERKS3_ Line | Count | Source | 161 | 510k | { | 162 | 510k | m_used -= del; | 163 | | // Remove now-unused trailing entries. | 164 | 1.43M | while (!entries.empty() && !m_used[entries.size() - 1]) { Branch (164:16): [True: 1.05M, False: 377k]
Branch (164:36): [True: 922k, False: 132k]
| 165 | 922k | entries.pop_back(); | 166 | 922k | } | 167 | | // Remove the deleted transactions from ancestors/descendants of other transactions. Note | 168 | | // that the deleted positions will retain old feerate and dependency information. This does | 169 | | // not matter as they will be overwritten by AddTransaction if they get used again. | 170 | 1.04M | for (auto& entry : entries) { Branch (170:26): [True: 1.04M, False: 510k]
| 171 | 1.04M | entry.ancestors &= m_used; | 172 | 1.04M | entry.descendants &= m_used; | 173 | 1.04M | } | 174 | 510k | } |
|
175 | | |
176 | | /** Modify this transaction graph, adding multiple parents to a specified child. |
177 | | * |
178 | | * Complexity: O(N) where N=TxCount(). |
179 | | */ |
180 | | void AddDependencies(const SetType& parents, DepGraphIndex child) noexcept |
181 | 4.20M | { |
182 | 4.20M | Assume(m_used[child]); |
183 | 4.20M | Assume(parents.IsSubsetOf(m_used)); |
184 | | // Compute the ancestors of parents that are not already ancestors of child. |
185 | 4.20M | SetType par_anc; |
186 | 4.20M | for (auto par : parents - Ancestors(child)) { Branch (186:23): [True: 195k, False: 111k]
Branch (186:23): [True: 560k, False: 851k]
Branch (186:23): [True: 1.80M, False: 3.24M]
|
187 | 2.56M | par_anc |= Ancestors(par); |
188 | 2.56M | } |
189 | 4.20M | par_anc -= Ancestors(child); |
190 | | // Bail out if there are no such ancestors. |
191 | 4.20M | if (par_anc.None()) return; Branch (191:13): [True: 61.5k, False: 50.2k]
Branch (191:13): [True: 290k, False: 560k]
Branch (191:13): [True: 1.79M, False: 1.44M]
|
192 | | // To each such ancestor, add as descendants the descendants of the child. |
193 | 2.05M | const auto& chl_des = entries[child].descendants; |
194 | 5.12M | for (auto anc_of_par : par_anc) { Branch (194:30): [True: 335k, False: 50.2k]
Branch (194:30): [True: 1.00M, False: 560k]
Branch (194:30): [True: 3.77M, False: 1.44M]
|
195 | 5.12M | entries[anc_of_par].descendants |= chl_des; |
196 | 5.12M | } |
197 | | // To each descendant of the child, add those ancestors. |
198 | 2.81M | for (auto dec_of_chl : Descendants(child)) { Branch (198:30): [True: 59.8k, False: 50.2k]
Branch (198:30): [True: 1.08M, False: 560k]
Branch (198:30): [True: 1.66M, False: 1.44M]
|
199 | 2.81M | entries[dec_of_chl].ancestors |= par_anc; |
200 | 2.81M | } |
201 | 2.05M | } _ZN17cluster_linearize8DepGraphIN13bitset_detail9IntBitSetIjEEE15AddDependenciesERKS3_j Line | Count | Source | 181 | 111k | { | 182 | 111k | Assume(m_used[child]); | 183 | 111k | Assume(parents.IsSubsetOf(m_used)); | 184 | | // Compute the ancestors of parents that are not already ancestors of child. | 185 | 111k | SetType par_anc; | 186 | 195k | for (auto par : parents - Ancestors(child)) { Branch (186:23): [True: 195k, False: 111k]
| 187 | 195k | par_anc |= Ancestors(par); | 188 | 195k | } | 189 | 111k | par_anc -= Ancestors(child); | 190 | | // Bail out if there are no such ancestors. | 191 | 111k | if (par_anc.None()) return; Branch (191:13): [True: 61.5k, False: 50.2k]
| 192 | | // To each such ancestor, add as descendants the descendants of the child. | 193 | 50.2k | const auto& chl_des = entries[child].descendants; | 194 | 335k | for (auto anc_of_par : par_anc) { Branch (194:30): [True: 335k, False: 50.2k]
| 195 | 335k | entries[anc_of_par].descendants |= chl_des; | 196 | 335k | } | 197 | | // To each descendant of the child, add those ancestors. | 198 | 59.8k | for (auto dec_of_chl : Descendants(child)) { Branch (198:30): [True: 59.8k, False: 50.2k]
| 199 | 59.8k | entries[dec_of_chl].ancestors |= par_anc; | 200 | 59.8k | } | 201 | 50.2k | } |
_ZN17cluster_linearize8DepGraphIN13bitset_detail14MultiIntBitSetImLj2EEEE15AddDependenciesERKS3_j Line | Count | Source | 181 | 851k | { | 182 | 851k | Assume(m_used[child]); | 183 | 851k | Assume(parents.IsSubsetOf(m_used)); | 184 | | // Compute the ancestors of parents that are not already ancestors of child. | 185 | 851k | SetType par_anc; | 186 | 851k | for (auto par : parents - Ancestors(child)) { Branch (186:23): [True: 560k, False: 851k]
| 187 | 560k | par_anc |= Ancestors(par); | 188 | 560k | } | 189 | 851k | par_anc -= Ancestors(child); | 190 | | // Bail out if there are no such ancestors. | 191 | 851k | if (par_anc.None()) return; Branch (191:13): [True: 290k, False: 560k]
| 192 | | // To each such ancestor, add as descendants the descendants of the child. | 193 | 560k | const auto& chl_des = entries[child].descendants; | 194 | 1.00M | for (auto anc_of_par : par_anc) { Branch (194:30): [True: 1.00M, False: 560k]
| 195 | 1.00M | entries[anc_of_par].descendants |= chl_des; | 196 | 1.00M | } | 197 | | // To each descendant of the child, add those ancestors. | 198 | 1.08M | for (auto dec_of_chl : Descendants(child)) { Branch (198:30): [True: 1.08M, False: 560k]
| 199 | 1.08M | entries[dec_of_chl].ancestors |= par_anc; | 200 | 1.08M | } | 201 | 560k | } |
_ZN17cluster_linearize8DepGraphIN13bitset_detail9IntBitSetImEEE15AddDependenciesERKS3_j Line | Count | Source | 181 | 3.24M | { | 182 | 3.24M | Assume(m_used[child]); | 183 | 3.24M | Assume(parents.IsSubsetOf(m_used)); | 184 | | // Compute the ancestors of parents that are not already ancestors of child. | 185 | 3.24M | SetType par_anc; | 186 | 3.24M | for (auto par : parents - Ancestors(child)) { Branch (186:23): [True: 1.80M, False: 3.24M]
| 187 | 1.80M | par_anc |= Ancestors(par); | 188 | 1.80M | } | 189 | 3.24M | par_anc -= Ancestors(child); | 190 | | // Bail out if there are no such ancestors. | 191 | 3.24M | if (par_anc.None()) return; Branch (191:13): [True: 1.79M, False: 1.44M]
| 192 | | // To each such ancestor, add as descendants the descendants of the child. | 193 | 1.44M | const auto& chl_des = entries[child].descendants; | 194 | 3.77M | for (auto anc_of_par : par_anc) { Branch (194:30): [True: 3.77M, False: 1.44M]
| 195 | 3.77M | entries[anc_of_par].descendants |= chl_des; | 196 | 3.77M | } | 197 | | // To each descendant of the child, add those ancestors. | 198 | 1.66M | for (auto dec_of_chl : Descendants(child)) { Branch (198:30): [True: 1.66M, False: 1.44M]
| 199 | 1.66M | entries[dec_of_chl].ancestors |= par_anc; | 200 | 1.66M | } | 201 | 1.44M | } |
|
202 | | |
203 | | /** Compute the (reduced) set of parents of node i in this graph. |
204 | | * |
205 | | * This returns the minimal subset of the parents of i whose ancestors together equal all of |
206 | | * i's ancestors (unless i is part of a cycle of dependencies). Note that DepGraph does not |
207 | | * store the set of parents; this information is inferred from the ancestor sets. |
208 | | * |
209 | | * Complexity: O(N) where N=Ancestors(i).Count() (which is bounded by TxCount()). |
210 | | */ |
211 | | SetType GetReducedParents(DepGraphIndex i) const noexcept |
212 | 7.62M | { |
213 | 7.62M | SetType parents = Ancestors(i); |
214 | 7.62M | parents.Reset(i); |
215 | 20.8M | for (auto parent : parents) { Branch (215:26): [True: 487k, False: 141k]
Branch (215:26): [True: 48.1k, False: 42.3k]
Branch (215:26): [True: 20.2M, False: 7.43M]
|
216 | 20.8M | if (parents[parent]) { Branch (216:17): [True: 400k, False: 86.9k]
Branch (216:17): [True: 31.7k, False: 16.4k]
Branch (216:17): [True: 19.2M, False: 1.02M]
|
217 | 19.6M | parents -= Ancestors(parent); |
218 | 19.6M | parents.Set(parent); |
219 | 19.6M | } |
220 | 20.8M | } |
221 | 7.62M | return parents; |
222 | 7.62M | } _ZNK17cluster_linearize8DepGraphIN13bitset_detail9IntBitSetIjEEE17GetReducedParentsEj Line | Count | Source | 212 | 141k | { | 213 | 141k | SetType parents = Ancestors(i); | 214 | 141k | parents.Reset(i); | 215 | 487k | for (auto parent : parents) { Branch (215:26): [True: 487k, False: 141k]
| 216 | 487k | if (parents[parent]) { Branch (216:17): [True: 400k, False: 86.9k]
| 217 | 400k | parents -= Ancestors(parent); | 218 | 400k | parents.Set(parent); | 219 | 400k | } | 220 | 487k | } | 221 | 141k | return parents; | 222 | 141k | } |
_ZNK17cluster_linearize8DepGraphIN13bitset_detail14MultiIntBitSetImLj2EEEE17GetReducedParentsEj Line | Count | Source | 212 | 42.3k | { | 213 | 42.3k | SetType parents = Ancestors(i); | 214 | 42.3k | parents.Reset(i); | 215 | 48.1k | for (auto parent : parents) { Branch (215:26): [True: 48.1k, False: 42.3k]
| 216 | 48.1k | if (parents[parent]) { Branch (216:17): [True: 31.7k, False: 16.4k]
| 217 | 31.7k | parents -= Ancestors(parent); | 218 | 31.7k | parents.Set(parent); | 219 | 31.7k | } | 220 | 48.1k | } | 221 | 42.3k | return parents; | 222 | 42.3k | } |
_ZNK17cluster_linearize8DepGraphIN13bitset_detail9IntBitSetImEEE17GetReducedParentsEj Line | Count | Source | 212 | 7.43M | { | 213 | 7.43M | SetType parents = Ancestors(i); | 214 | 7.43M | parents.Reset(i); | 215 | 20.2M | for (auto parent : parents) { Branch (215:26): [True: 20.2M, False: 7.43M]
| 216 | 20.2M | if (parents[parent]) { Branch (216:17): [True: 19.2M, False: 1.02M]
| 217 | 19.2M | parents -= Ancestors(parent); | 218 | 19.2M | parents.Set(parent); | 219 | 19.2M | } | 220 | 20.2M | } | 221 | 7.43M | return parents; | 222 | 7.43M | } |
|
223 | | |
224 | | /** Compute the (reduced) set of children of node i in this graph. |
225 | | * |
226 | | * This returns the minimal subset of the children of i whose descendants together equal all of |
227 | | * i's descendants (unless i is part of a cycle of dependencies). Note that DepGraph does not |
228 | | * store the set of children; this information is inferred from the descendant sets. |
229 | | * |
230 | | * Complexity: O(N) where N=Descendants(i).Count() (which is bounded by TxCount()). |
231 | | */ |
232 | | SetType GetReducedChildren(DepGraphIndex i) const noexcept |
233 | 115k | { |
234 | 115k | SetType children = Descendants(i); |
235 | 115k | children.Reset(i); |
236 | 441k | for (auto child : children) { Branch (236:25): [True: 441k, False: 115k]
|
237 | 441k | if (children[child]) { Branch (237:17): [True: 168k, False: 273k]
|
238 | 168k | children -= Descendants(child); |
239 | 168k | children.Set(child); |
240 | 168k | } |
241 | 441k | } |
242 | 115k | return children; |
243 | 115k | } |
244 | | |
245 | | /** Compute the aggregate feerate of a set of nodes in this graph. |
246 | | * |
247 | | * Complexity: O(N) where N=elems.Count(). |
248 | | **/ |
249 | | FeeFrac FeeRate(const SetType& elems) const noexcept |
250 | 17.8M | { |
251 | 17.8M | FeeFrac ret; |
252 | 303M | for (auto pos : elems) ret += entries[pos].feerate; Branch (252:23): [True: 303M, False: 17.8M]
Branch (252:23): [True: 14.8k, False: 1.06k]
|
253 | 17.8M | return ret; |
254 | 17.8M | } _ZNK17cluster_linearize8DepGraphIN13bitset_detail9IntBitSetIjEEE7FeeRateERKS3_ Line | Count | Source | 250 | 17.8M | { | 251 | 17.8M | FeeFrac ret; | 252 | 303M | for (auto pos : elems) ret += entries[pos].feerate; Branch (252:23): [True: 303M, False: 17.8M]
| 253 | 17.8M | return ret; | 254 | 17.8M | } |
_ZNK17cluster_linearize8DepGraphIN13bitset_detail14MultiIntBitSetImLj2EEEE7FeeRateERKS3_ Line | Count | Source | 250 | 1.06k | { | 251 | 1.06k | FeeFrac ret; | 252 | 14.8k | for (auto pos : elems) ret += entries[pos].feerate; Branch (252:23): [True: 14.8k, False: 1.06k]
| 253 | 1.06k | return ret; | 254 | 1.06k | } |
|
255 | | |
256 | | /** Get the connected component within the subset "todo" that contains tx (which must be in |
257 | | * todo). |
258 | | * |
259 | | * Two transactions are considered connected if they are both in `todo`, and one is an ancestor |
260 | | * of the other in the entire graph (so not just within `todo`), or transitively there is a |
261 | | * path of transactions connecting them. This does mean that if `todo` contains a transaction |
262 | | * and a grandparent, but misses the parent, they will still be part of the same component. |
263 | | * |
264 | | * Complexity: O(ret.Count()). |
265 | | */ |
266 | | SetType GetConnectedComponent(const SetType& todo, DepGraphIndex tx) const noexcept |
267 | 5.39M | { |
268 | 5.39M | Assume(todo[tx]); |
269 | 5.39M | Assume(todo.IsSubsetOf(m_used)); |
270 | 5.39M | auto to_add = SetType::Singleton(tx); |
271 | 5.39M | SetType ret; |
272 | 12.0M | do { |
273 | 12.0M | SetType old = ret; |
274 | 24.6M | for (auto add : to_add) { Branch (274:27): [True: 52.3k, False: 29.8k]
Branch (274:27): [True: 22.2M, False: 10.9M]
Branch (274:27): [True: 2.38M, False: 1.07M]
|
275 | 24.6M | ret |= Descendants(add); |
276 | 24.6M | ret |= Ancestors(add); |
277 | 24.6M | } |
278 | 12.0M | ret &= todo; |
279 | 12.0M | to_add = ret - old; |
280 | 12.0M | } while (to_add.Any()); Branch (280:18): [True: 16.3k, False: 13.5k]
Branch (280:18): [True: 6.07M, False: 4.89M]
Branch (280:18): [True: 589k, False: 484k]
|
281 | 5.39M | return ret; |
282 | 5.39M | } _ZNK17cluster_linearize8DepGraphIN13bitset_detail9IntBitSetIjEEE21GetConnectedComponentERKS3_j Line | Count | Source | 267 | 13.5k | { | 268 | 13.5k | Assume(todo[tx]); | 269 | 13.5k | Assume(todo.IsSubsetOf(m_used)); | 270 | 13.5k | auto to_add = SetType::Singleton(tx); | 271 | 13.5k | SetType ret; | 272 | 29.8k | do { | 273 | 29.8k | SetType old = ret; | 274 | 52.3k | for (auto add : to_add) { Branch (274:27): [True: 52.3k, False: 29.8k]
| 275 | 52.3k | ret |= Descendants(add); | 276 | 52.3k | ret |= Ancestors(add); | 277 | 52.3k | } | 278 | 29.8k | ret &= todo; | 279 | 29.8k | to_add = ret - old; | 280 | 29.8k | } while (to_add.Any()); Branch (280:18): [True: 16.3k, False: 13.5k]
| 281 | 13.5k | return ret; | 282 | 13.5k | } |
_ZNK17cluster_linearize8DepGraphIN13bitset_detail14MultiIntBitSetImLj2EEEE21GetConnectedComponentERKS3_j Line | Count | Source | 267 | 4.89M | { | 268 | 4.89M | Assume(todo[tx]); | 269 | 4.89M | Assume(todo.IsSubsetOf(m_used)); | 270 | 4.89M | auto to_add = SetType::Singleton(tx); | 271 | 4.89M | SetType ret; | 272 | 10.9M | do { | 273 | 10.9M | SetType old = ret; | 274 | 22.2M | for (auto add : to_add) { Branch (274:27): [True: 22.2M, False: 10.9M]
| 275 | 22.2M | ret |= Descendants(add); | 276 | 22.2M | ret |= Ancestors(add); | 277 | 22.2M | } | 278 | 10.9M | ret &= todo; | 279 | 10.9M | to_add = ret - old; | 280 | 10.9M | } while (to_add.Any()); Branch (280:18): [True: 6.07M, False: 4.89M]
| 281 | 4.89M | return ret; | 282 | 4.89M | } |
_ZNK17cluster_linearize8DepGraphIN13bitset_detail9IntBitSetImEEE21GetConnectedComponentERKS3_j Line | Count | Source | 267 | 484k | { | 268 | 484k | Assume(todo[tx]); | 269 | 484k | Assume(todo.IsSubsetOf(m_used)); | 270 | 484k | auto to_add = SetType::Singleton(tx); | 271 | 484k | SetType ret; | 272 | 1.07M | do { | 273 | 1.07M | SetType old = ret; | 274 | 2.38M | for (auto add : to_add) { Branch (274:27): [True: 2.38M, False: 1.07M]
| 275 | 2.38M | ret |= Descendants(add); | 276 | 2.38M | ret |= Ancestors(add); | 277 | 2.38M | } | 278 | 1.07M | ret &= todo; | 279 | 1.07M | to_add = ret - old; | 280 | 1.07M | } while (to_add.Any()); Branch (280:18): [True: 589k, False: 484k]
| 281 | 484k | return ret; | 282 | 484k | } |
|
283 | | |
284 | | /** Find some connected component within the subset "todo" of this graph. |
285 | | * |
286 | | * Specifically, this finds the connected component which contains the first transaction of |
287 | | * todo (if any). |
288 | | * |
289 | | * Complexity: O(ret.Count()). |
290 | | */ |
291 | | SetType FindConnectedComponent(const SetType& todo) const noexcept |
292 | 4.04M | { |
293 | 4.04M | if (todo.None()) return todo; Branch (293:13): [True: 81, False: 13.4k]
Branch (293:13): [True: 2.42k, False: 3.54M]
Branch (293:13): [True: 0, False: 484k]
|
294 | 4.04M | return GetConnectedComponent(todo, todo.First()); |
295 | 4.04M | } _ZNK17cluster_linearize8DepGraphIN13bitset_detail9IntBitSetIjEEE22FindConnectedComponentERKS3_ Line | Count | Source | 292 | 13.4k | { | 293 | 13.4k | if (todo.None()) return todo; Branch (293:13): [True: 81, False: 13.4k]
| 294 | 13.4k | return GetConnectedComponent(todo, todo.First()); | 295 | 13.4k | } |
_ZNK17cluster_linearize8DepGraphIN13bitset_detail14MultiIntBitSetImLj2EEEE22FindConnectedComponentERKS3_ Line | Count | Source | 292 | 3.54M | { | 293 | 3.54M | if (todo.None()) return todo; Branch (293:13): [True: 2.42k, False: 3.54M]
| 294 | 3.54M | return GetConnectedComponent(todo, todo.First()); | 295 | 3.54M | } |
_ZNK17cluster_linearize8DepGraphIN13bitset_detail9IntBitSetImEEE22FindConnectedComponentERKS3_ Line | Count | Source | 292 | 484k | { | 293 | 484k | if (todo.None()) return todo; Branch (293:13): [True: 0, False: 484k]
| 294 | 484k | return GetConnectedComponent(todo, todo.First()); | 295 | 484k | } |
|
296 | | |
297 | | /** Determine if a subset is connected. |
298 | | * |
299 | | * Complexity: O(subset.Count()). |
300 | | */ |
301 | | bool IsConnected(const SetType& subset) const noexcept |
302 | 1.23M | { |
303 | 1.23M | return FindConnectedComponent(subset) == subset; |
304 | 1.23M | } _ZNK17cluster_linearize8DepGraphIN13bitset_detail9IntBitSetIjEEE11IsConnectedERKS3_ Line | Count | Source | 302 | 7.61k | { | 303 | 7.61k | return FindConnectedComponent(subset) == subset; | 304 | 7.61k | } |
_ZNK17cluster_linearize8DepGraphIN13bitset_detail14MultiIntBitSetImLj2EEEE11IsConnectedERKS3_ Line | Count | Source | 302 | 914k | { | 303 | 914k | return FindConnectedComponent(subset) == subset; | 304 | 914k | } |
_ZNK17cluster_linearize8DepGraphIN13bitset_detail9IntBitSetImEEE11IsConnectedERKS3_ Line | Count | Source | 302 | 310k | { | 303 | 310k | return FindConnectedComponent(subset) == subset; | 304 | 310k | } |
|
305 | | |
306 | | /** Determine if this entire graph is connected. |
307 | | * |
308 | | * Complexity: O(TxCount()). |
309 | | */ |
310 | 193 | bool IsConnected() const noexcept { return IsConnected(m_used); } |
311 | | |
312 | | /** Append the entries of select to list in a topologically valid order. |
313 | | * |
314 | | * Complexity: O(select.Count() * log(select.Count())). |
315 | | */ |
316 | | void AppendTopo(std::vector<DepGraphIndex>& list, const SetType& select) const noexcept |
317 | 10.4k | { |
318 | 10.4k | DepGraphIndex old_len = list.size(); |
319 | 18.5k | for (auto i : select) list.push_back(i); Branch (319:21): [True: 18.5k, False: 10.4k]
|
320 | 42.7k | std::ranges::sort(std::span{list}.subspan(old_len), [&](DepGraphIndex a, DepGraphIndex b) noexcept { |
321 | 42.7k | const auto a_anc_count = entries[a].ancestors.Count(); |
322 | 42.7k | const auto b_anc_count = entries[b].ancestors.Count(); |
323 | 42.7k | if (a_anc_count != b_anc_count) return a_anc_count < b_anc_count; Branch (323:17): [True: 29.5k, False: 13.2k]
|
324 | 13.2k | return a < b; |
325 | 42.7k | }); |
326 | 10.4k | } |
327 | | |
328 | | /** Check if this graph is acyclic. */ |
329 | | bool IsAcyclic() const noexcept |
330 | 69.5k | { |
331 | 365k | for (auto i : Positions()) { Branch (331:21): [True: 9.81k, False: 569]
Branch (331:21): [True: 355k, False: 68.9k]
|
332 | 365k | if ((Ancestors(i) & Descendants(i)) != SetType::Singleton(i)) { Branch (332:17): [True: 83, False: 9.72k]
Branch (332:17): [True: 0, False: 355k]
|
333 | 83 | return false; |
334 | 83 | } |
335 | 365k | } |
336 | 69.5k | return true; |
337 | 69.5k | } _ZNK17cluster_linearize8DepGraphIN13bitset_detail9IntBitSetIjEEE9IsAcyclicEv Line | Count | Source | 330 | 652 | { | 331 | 9.81k | for (auto i : Positions()) { Branch (331:21): [True: 9.81k, False: 569]
| 332 | 9.81k | if ((Ancestors(i) & Descendants(i)) != SetType::Singleton(i)) { Branch (332:17): [True: 83, False: 9.72k]
| 333 | 83 | return false; | 334 | 83 | } | 335 | 9.81k | } | 336 | 569 | return true; | 337 | 652 | } |
_ZNK17cluster_linearize8DepGraphIN13bitset_detail9IntBitSetImEEE9IsAcyclicEv Line | Count | Source | 330 | 68.9k | { | 331 | 355k | for (auto i : Positions()) { Branch (331:21): [True: 355k, False: 68.9k]
| 332 | 355k | if ((Ancestors(i) & Descendants(i)) != SetType::Singleton(i)) { Branch (332:17): [True: 0, False: 355k]
| 333 | 0 | return false; | 334 | 0 | } | 335 | 355k | } | 336 | 68.9k | return true; | 337 | 68.9k | } |
|
338 | | |
339 | | unsigned CountDependencies() const noexcept |
340 | | { |
341 | | unsigned ret = 0; |
342 | | for (auto i : Positions()) { |
343 | | ret += GetReducedParents(i).Count(); |
344 | | } |
345 | | return ret; |
346 | | } |
347 | | |
348 | | /** Reduce memory usage if possible. No observable effect. */ |
349 | | void Compact() noexcept |
350 | 1.33M | { |
351 | 1.33M | entries.shrink_to_fit(); |
352 | 1.33M | } _ZN17cluster_linearize8DepGraphIN13bitset_detail9IntBitSetIjEEE7CompactEv Line | Count | Source | 350 | 7.42k | { | 351 | 7.42k | entries.shrink_to_fit(); | 352 | 7.42k | } |
_ZN17cluster_linearize8DepGraphIN13bitset_detail9IntBitSetImEEE7CompactEv Line | Count | Source | 350 | 1.32M | { | 351 | 1.32M | entries.shrink_to_fit(); | 352 | 1.32M | } |
|
353 | | |
354 | | size_t DynamicMemoryUsage() const noexcept |
355 | 3.18M | { |
356 | 3.18M | return memusage::DynamicUsage(entries); |
357 | 3.18M | } _ZNK17cluster_linearize8DepGraphIN13bitset_detail9IntBitSetIjEEE18DynamicMemoryUsageEv Line | Count | Source | 355 | 14.8k | { | 356 | 14.8k | return memusage::DynamicUsage(entries); | 357 | 14.8k | } |
_ZNK17cluster_linearize8DepGraphIN13bitset_detail9IntBitSetImEEE18DynamicMemoryUsageEv Line | Count | Source | 355 | 3.17M | { | 356 | 3.17M | return memusage::DynamicUsage(entries); | 357 | 3.17M | } |
|
358 | | }; |
359 | | |
360 | | /** A set of transactions together with their aggregate feerate. */ |
361 | | template<typename SetType> |
362 | | struct SetInfo |
363 | | { |
364 | | /** The transactions in the set. */ |
365 | | SetType transactions; |
366 | | /** Their combined fee and size. */ |
367 | | FeeFrac feerate; |
368 | | |
369 | | /** Construct a SetInfo for the empty set. */ |
370 | 6.71M | SetInfo() noexcept = default; _ZN17cluster_linearize7SetInfoIN13bitset_detail9IntBitSetIjEEEC2Ev Line | Count | Source | 370 | 27.9k | SetInfo() noexcept = default; |
_ZN17cluster_linearize7SetInfoIN13bitset_detail14MultiIntBitSetImLj2EEEEC2Ev Line | Count | Source | 370 | 21.1k | SetInfo() noexcept = default; |
_ZN17cluster_linearize7SetInfoIN13bitset_detail9IntBitSetImEEEC2Ev Line | Count | Source | 370 | 6.66M | SetInfo() noexcept = default; |
|
371 | | |
372 | | /** Construct a SetInfo for a specified set and feerate. */ |
373 | 583 | SetInfo(const SetType& txn, const FeeFrac& fr) noexcept : transactions(txn), feerate(fr) {} |
374 | | |
375 | | /** Construct a SetInfo for a given transaction in a depgraph. */ |
376 | | explicit SetInfo(const DepGraph<SetType>& depgraph, DepGraphIndex pos) noexcept : |
377 | 14.8M | transactions(SetType::Singleton(pos)), feerate(depgraph.FeeRate(pos)) {}_ZN17cluster_linearize7SetInfoIN13bitset_detail9IntBitSetIjEEEC2ERKNS_8DepGraphIS3_EEj Line | Count | Source | 377 | 35.2k | transactions(SetType::Singleton(pos)), feerate(depgraph.FeeRate(pos)) {} |
_ZN17cluster_linearize7SetInfoIN13bitset_detail14MultiIntBitSetImLj2EEEEC2ERKNS_8DepGraphIS3_EEj Line | Count | Source | 377 | 1.60M | transactions(SetType::Singleton(pos)), feerate(depgraph.FeeRate(pos)) {} |
_ZN17cluster_linearize7SetInfoIN13bitset_detail9IntBitSetImEEEC2ERKNS_8DepGraphIS3_EEj Line | Count | Source | 377 | 13.2M | transactions(SetType::Singleton(pos)), feerate(depgraph.FeeRate(pos)) {} |
|
378 | | |
379 | | /** Construct a SetInfo for a set of transactions in a depgraph. */ |
380 | | explicit SetInfo(const DepGraph<SetType>& depgraph, const SetType& txn) noexcept : |
381 | 17.8M | transactions(txn), feerate(depgraph.FeeRate(txn)) {} |
382 | | |
383 | | /** Add a transaction to this SetInfo (which must not yet be in it). */ |
384 | | void Set(const DepGraph<SetType>& depgraph, DepGraphIndex pos) noexcept |
385 | 2.83k | { |
386 | 2.83k | Assume(!transactions[pos]); |
387 | 2.83k | transactions.Set(pos); |
388 | 2.83k | feerate += depgraph.FeeRate(pos); |
389 | 2.83k | } |
390 | | |
391 | | /** Add the transactions of other to this SetInfo (no overlap allowed). */ |
392 | | SetInfo& operator|=(const SetInfo& other) noexcept |
393 | 16.1M | { |
394 | 16.1M | Assume(!transactions.Overlaps(other.transactions)); |
395 | 16.1M | transactions |= other.transactions; |
396 | 16.1M | feerate += other.feerate; |
397 | 16.1M | return *this; |
398 | 16.1M | } _ZN17cluster_linearize7SetInfoIN13bitset_detail9IntBitSetIjEEEoRERKS4_ Line | Count | Source | 393 | 105k | { | 394 | 105k | Assume(!transactions.Overlaps(other.transactions)); | 395 | 105k | transactions |= other.transactions; | 396 | 105k | feerate += other.feerate; | 397 | 105k | return *this; | 398 | 105k | } |
_ZN17cluster_linearize7SetInfoIN13bitset_detail14MultiIntBitSetImLj2EEEEoRERKS4_ Line | Count | Source | 393 | 678k | { | 394 | 678k | Assume(!transactions.Overlaps(other.transactions)); | 395 | 678k | transactions |= other.transactions; | 396 | 678k | feerate += other.feerate; | 397 | 678k | return *this; | 398 | 678k | } |
_ZN17cluster_linearize7SetInfoIN13bitset_detail9IntBitSetImEEEoRERKS4_ Line | Count | Source | 393 | 15.3M | { | 394 | 15.3M | Assume(!transactions.Overlaps(other.transactions)); | 395 | 15.3M | transactions |= other.transactions; | 396 | 15.3M | feerate += other.feerate; | 397 | 15.3M | return *this; | 398 | 15.3M | } |
|
399 | | |
400 | | /** Remove the transactions of other from this SetInfo (which must be a subset). */ |
401 | | SetInfo& operator-=(const SetInfo& other) noexcept |
402 | 4.80M | { |
403 | 4.80M | Assume(other.transactions.IsSubsetOf(transactions)); |
404 | 4.80M | transactions -= other.transactions; |
405 | 4.80M | feerate -= other.feerate; |
406 | 4.80M | return *this; |
407 | 4.80M | } _ZN17cluster_linearize7SetInfoIN13bitset_detail9IntBitSetIjEEEmIERKS4_ Line | Count | Source | 402 | 60.2k | { | 403 | 60.2k | Assume(other.transactions.IsSubsetOf(transactions)); | 404 | 60.2k | transactions -= other.transactions; | 405 | 60.2k | feerate -= other.feerate; | 406 | 60.2k | return *this; | 407 | 60.2k | } |
_ZN17cluster_linearize7SetInfoIN13bitset_detail14MultiIntBitSetImLj2EEEEmIERKS4_ Line | Count | Source | 402 | 13.4k | { | 403 | 13.4k | Assume(other.transactions.IsSubsetOf(transactions)); | 404 | 13.4k | transactions -= other.transactions; | 405 | 13.4k | feerate -= other.feerate; | 406 | 13.4k | return *this; | 407 | 13.4k | } |
_ZN17cluster_linearize7SetInfoIN13bitset_detail9IntBitSetImEEEmIERKS4_ Line | Count | Source | 402 | 4.73M | { | 403 | 4.73M | Assume(other.transactions.IsSubsetOf(transactions)); | 404 | 4.73M | transactions -= other.transactions; | 405 | 4.73M | feerate -= other.feerate; | 406 | 4.73M | return *this; | 407 | 4.73M | } |
|
408 | | |
409 | | /** Compute the difference between this and other SetInfo (which must be a subset). */ |
410 | | SetInfo operator-(const SetInfo& other) const noexcept |
411 | | { |
412 | | Assume(other.transactions.IsSubsetOf(transactions)); |
413 | | return {transactions - other.transactions, feerate - other.feerate}; |
414 | | } |
415 | | |
416 | | /** Swap two SetInfo objects. */ |
417 | | friend void swap(SetInfo& a, SetInfo& b) noexcept |
418 | | { |
419 | | swap(a.transactions, b.transactions); |
420 | | swap(a.feerate, b.feerate); |
421 | | } |
422 | | |
423 | | /** Permit equality testing. */ |
424 | 2.46k | friend bool operator==(const SetInfo&, const SetInfo&) noexcept = default; Branch (424:77): [True: 822, False: 0]
Branch (424:77): [True: 822, False: 0]
|
425 | | }; |
426 | | |
427 | | /** Compute the chunks of linearization as SetInfos. */ |
428 | | template<typename SetType> |
429 | | std::vector<SetInfo<SetType>> ChunkLinearizationInfo(const DepGraph<SetType>& depgraph, std::span<const DepGraphIndex> linearization) noexcept |
430 | 1.49M | { |
431 | 1.49M | std::vector<SetInfo<SetType>> ret; |
432 | 8.17M | for (DepGraphIndex i : linearization) { Branch (432:26): [True: 7.80k, False: 401]
Branch (432:26): [True: 1.58M, False: 123k]
Branch (432:26): [True: 6.57M, False: 1.36M]
|
433 | | /** The new chunk to be added, initially a singleton. */ |
434 | 8.17M | SetInfo<SetType> new_chunk(depgraph, i); |
435 | | // As long as the new chunk has a higher feerate than the last chunk so far, absorb it. |
436 | 10.7M | while (!ret.empty() && ByRatio{new_chunk.feerate} > ByRatio{ret.back().feerate}) { Branch (436:16): [True: 10.9k, False: 1.05k]
Branch (436:16): [True: 4.22k, False: 7.80k]
Branch (436:32): [True: 4.22k, False: 6.74k]
Branch (436:16): [True: 2.08M, False: 160k]
Branch (436:16): [True: 657k, False: 1.58M]
Branch (436:32): [True: 657k, False: 1.42M]
Branch (436:16): [True: 6.49M, False: 2.04M]
Branch (436:16): [True: 1.95M, False: 6.57M]
Branch (436:32): [True: 1.95M, False: 4.53M]
|
437 | 2.62M | new_chunk |= ret.back(); |
438 | 2.62M | ret.pop_back(); |
439 | 2.62M | } |
440 | | // Actually move that new chunk into the chunking. |
441 | 8.17M | ret.emplace_back(std::move(new_chunk)); |
442 | 8.17M | } |
443 | 1.49M | return ret; |
444 | 1.49M | } _ZN17cluster_linearize22ChunkLinearizationInfoIN13bitset_detail9IntBitSetIjEEEESt6vectorINS_7SetInfoIT_EESaIS7_EERKNS_8DepGraphIS6_EESt4spanIKjLm18446744073709551615EE Line | Count | Source | 430 | 401 | { | 431 | 401 | std::vector<SetInfo<SetType>> ret; | 432 | 7.80k | for (DepGraphIndex i : linearization) { Branch (432:26): [True: 7.80k, False: 401]
| 433 | | /** The new chunk to be added, initially a singleton. */ | 434 | 7.80k | SetInfo<SetType> new_chunk(depgraph, i); | 435 | | // As long as the new chunk has a higher feerate than the last chunk so far, absorb it. | 436 | 12.0k | while (!ret.empty() && ByRatio{new_chunk.feerate} > ByRatio{ret.back().feerate}) { Branch (436:16): [True: 10.9k, False: 1.05k]
Branch (436:16): [True: 4.22k, False: 7.80k]
Branch (436:32): [True: 4.22k, False: 6.74k]
| 437 | 4.22k | new_chunk |= ret.back(); | 438 | 4.22k | ret.pop_back(); | 439 | 4.22k | } | 440 | | // Actually move that new chunk into the chunking. | 441 | 7.80k | ret.emplace_back(std::move(new_chunk)); | 442 | 7.80k | } | 443 | 401 | return ret; | 444 | 401 | } |
_ZN17cluster_linearize22ChunkLinearizationInfoIN13bitset_detail14MultiIntBitSetImLj2EEEEESt6vectorINS_7SetInfoIT_EESaIS7_EERKNS_8DepGraphIS6_EESt4spanIKjLm18446744073709551615EE Line | Count | Source | 430 | 123k | { | 431 | 123k | std::vector<SetInfo<SetType>> ret; | 432 | 1.58M | for (DepGraphIndex i : linearization) { Branch (432:26): [True: 1.58M, False: 123k]
| 433 | | /** The new chunk to be added, initially a singleton. */ | 434 | 1.58M | SetInfo<SetType> new_chunk(depgraph, i); | 435 | | // As long as the new chunk has a higher feerate than the last chunk so far, absorb it. | 436 | 2.24M | while (!ret.empty() && ByRatio{new_chunk.feerate} > ByRatio{ret.back().feerate}) { Branch (436:16): [True: 2.08M, False: 160k]
Branch (436:16): [True: 657k, False: 1.58M]
Branch (436:32): [True: 657k, False: 1.42M]
| 437 | 657k | new_chunk |= ret.back(); | 438 | 657k | ret.pop_back(); | 439 | 657k | } | 440 | | // Actually move that new chunk into the chunking. | 441 | 1.58M | ret.emplace_back(std::move(new_chunk)); | 442 | 1.58M | } | 443 | 123k | return ret; | 444 | 123k | } |
_ZN17cluster_linearize22ChunkLinearizationInfoIN13bitset_detail9IntBitSetImEEEESt6vectorINS_7SetInfoIT_EESaIS7_EERKNS_8DepGraphIS6_EESt4spanIKjLm18446744073709551615EE Line | Count | Source | 430 | 1.36M | { | 431 | 1.36M | std::vector<SetInfo<SetType>> ret; | 432 | 6.57M | for (DepGraphIndex i : linearization) { Branch (432:26): [True: 6.57M, False: 1.36M]
| 433 | | /** The new chunk to be added, initially a singleton. */ | 434 | 6.57M | SetInfo<SetType> new_chunk(depgraph, i); | 435 | | // As long as the new chunk has a higher feerate than the last chunk so far, absorb it. | 436 | 8.53M | while (!ret.empty() && ByRatio{new_chunk.feerate} > ByRatio{ret.back().feerate}) { Branch (436:16): [True: 6.49M, False: 2.04M]
Branch (436:16): [True: 1.95M, False: 6.57M]
Branch (436:32): [True: 1.95M, False: 4.53M]
| 437 | 1.95M | new_chunk |= ret.back(); | 438 | 1.95M | ret.pop_back(); | 439 | 1.95M | } | 440 | | // Actually move that new chunk into the chunking. | 441 | 6.57M | ret.emplace_back(std::move(new_chunk)); | 442 | 6.57M | } | 443 | 1.36M | return ret; | 444 | 1.36M | } |
|
445 | | |
446 | | /** Compute the feerates of the chunks of linearization. Identical to ChunkLinearizationInfo, but |
447 | | * only returns the chunk feerates, not the corresponding transaction sets. */ |
448 | | template<typename SetType> |
449 | | std::vector<FeeFrac> ChunkLinearization(const DepGraph<SetType>& depgraph, std::span<const DepGraphIndex> linearization) noexcept |
450 | 1.47M | { |
451 | 1.47M | std::vector<FeeFrac> ret; |
452 | 7.31M | for (DepGraphIndex i : linearization) { Branch (452:26): [True: 3.24M, False: 391k]
Branch (452:26): [True: 252k, False: 80.0k]
Branch (452:26): [True: 3.81M, False: 1.00M]
|
453 | | /** The new chunk to be added, initially a singleton. */ |
454 | 7.31M | auto new_chunk = depgraph.FeeRate(i); |
455 | | // As long as the new chunk has a higher feerate than the last chunk so far, absorb it. |
456 | 10.3M | while (!ret.empty() && ByRatio{new_chunk} > ByRatio{ret.back()}) { Branch (456:16): [True: 4.14M, False: 974k]
Branch (456:16): [True: 1.87M, False: 3.24M]
Branch (456:32): [True: 1.87M, False: 2.27M]
Branch (456:16): [True: 207k, False: 83.0k]
Branch (456:16): [True: 37.8k, False: 252k]
Branch (456:32): [True: 37.8k, False: 169k]
Branch (456:16): [True: 3.45M, False: 1.49M]
Branch (456:16): [True: 1.13M, False: 3.81M]
Branch (456:32): [True: 1.13M, False: 2.31M]
|
457 | 3.05M | new_chunk += ret.back(); |
458 | 3.05M | ret.pop_back(); |
459 | 3.05M | } |
460 | | // Actually move that new chunk into the chunking. |
461 | 7.31M | ret.push_back(std::move(new_chunk)); |
462 | 7.31M | } |
463 | 1.47M | return ret; |
464 | 1.47M | } _ZN17cluster_linearize18ChunkLinearizationIN13bitset_detail9IntBitSetIjEEEESt6vectorI7FeeFracSaIS5_EERKNS_8DepGraphIT_EESt4spanIKjLm18446744073709551615EE Line | Count | Source | 450 | 391k | { | 451 | 391k | std::vector<FeeFrac> ret; | 452 | 3.24M | for (DepGraphIndex i : linearization) { Branch (452:26): [True: 3.24M, False: 391k]
| 453 | | /** The new chunk to be added, initially a singleton. */ | 454 | 3.24M | auto new_chunk = depgraph.FeeRate(i); | 455 | | // As long as the new chunk has a higher feerate than the last chunk so far, absorb it. | 456 | 5.12M | while (!ret.empty() && ByRatio{new_chunk} > ByRatio{ret.back()}) { Branch (456:16): [True: 4.14M, False: 974k]
Branch (456:16): [True: 1.87M, False: 3.24M]
Branch (456:32): [True: 1.87M, False: 2.27M]
| 457 | 1.87M | new_chunk += ret.back(); | 458 | 1.87M | ret.pop_back(); | 459 | 1.87M | } | 460 | | // Actually move that new chunk into the chunking. | 461 | 3.24M | ret.push_back(std::move(new_chunk)); | 462 | 3.24M | } | 463 | 391k | return ret; | 464 | 391k | } |
_ZN17cluster_linearize18ChunkLinearizationIN13bitset_detail14MultiIntBitSetImLj2EEEEESt6vectorI7FeeFracSaIS5_EERKNS_8DepGraphIT_EESt4spanIKjLm18446744073709551615EE Line | Count | Source | 450 | 80.0k | { | 451 | 80.0k | std::vector<FeeFrac> ret; | 452 | 252k | for (DepGraphIndex i : linearization) { Branch (452:26): [True: 252k, False: 80.0k]
| 453 | | /** The new chunk to be added, initially a singleton. */ | 454 | 252k | auto new_chunk = depgraph.FeeRate(i); | 455 | | // As long as the new chunk has a higher feerate than the last chunk so far, absorb it. | 456 | 290k | while (!ret.empty() && ByRatio{new_chunk} > ByRatio{ret.back()}) { Branch (456:16): [True: 207k, False: 83.0k]
Branch (456:16): [True: 37.8k, False: 252k]
Branch (456:32): [True: 37.8k, False: 169k]
| 457 | 37.8k | new_chunk += ret.back(); | 458 | 37.8k | ret.pop_back(); | 459 | 37.8k | } | 460 | | // Actually move that new chunk into the chunking. | 461 | 252k | ret.push_back(std::move(new_chunk)); | 462 | 252k | } | 463 | 80.0k | return ret; | 464 | 80.0k | } |
_ZN17cluster_linearize18ChunkLinearizationIN13bitset_detail9IntBitSetImEEEESt6vectorI7FeeFracSaIS5_EERKNS_8DepGraphIT_EESt4spanIKjLm18446744073709551615EE Line | Count | Source | 450 | 1.00M | { | 451 | 1.00M | std::vector<FeeFrac> ret; | 452 | 3.81M | for (DepGraphIndex i : linearization) { Branch (452:26): [True: 3.81M, False: 1.00M]
| 453 | | /** The new chunk to be added, initially a singleton. */ | 454 | 3.81M | auto new_chunk = depgraph.FeeRate(i); | 455 | | // As long as the new chunk has a higher feerate than the last chunk so far, absorb it. | 456 | 4.94M | while (!ret.empty() && ByRatio{new_chunk} > ByRatio{ret.back()}) { Branch (456:16): [True: 3.45M, False: 1.49M]
Branch (456:16): [True: 1.13M, False: 3.81M]
Branch (456:32): [True: 1.13M, False: 2.31M]
| 457 | 1.13M | new_chunk += ret.back(); | 458 | 1.13M | ret.pop_back(); | 459 | 1.13M | } | 460 | | // Actually move that new chunk into the chunking. | 461 | 3.81M | ret.push_back(std::move(new_chunk)); | 462 | 3.81M | } | 463 | 1.00M | return ret; | 464 | 1.00M | } |
|
465 | | |
466 | | /** Concept for function objects that return std::strong_ordering when invoked with two Args. */ |
467 | | template<typename F, typename Arg> |
468 | | concept StrongComparator = |
469 | | std::regular_invocable<F, Arg, Arg> && |
470 | | std::is_same_v<std::invoke_result_t<F, Arg, Arg>, std::strong_ordering>; |
471 | | |
472 | | /** Simple default transaction ordering function for SpanningForestState::GetLinearization() and |
473 | | * Linearize(), which just sorts by DepGraphIndex. */ |
474 | | using IndexTxOrder = std::compare_three_way; |
475 | | |
476 | | /** A default cost model for SFL for SetType=BitSet<64>, based on benchmarks. |
477 | | * |
478 | | * The numbers here were obtained in February 2026 by: |
479 | | * - For a variety of machines: |
480 | | * - Running a fixed collection of ~385000 clusters found through random generation and fuzzing, |
481 | | * optimizing for difficulty of linearization. |
482 | | * - Linearize each ~3000 times, with different random seeds. Sometimes without input |
483 | | * linearization, sometimes with a bad one. |
484 | | * - Gather cycle counts for each of the operations included in this cost model, |
485 | | * broken down by their parameters. |
486 | | * - Correct the data by subtracting the runtime of obtaining the cycle count. |
487 | | * - Drop the 5% top and bottom samples from each cycle count dataset, and compute the average |
488 | | * of the remaining samples. |
489 | | * - For each operation, fit a least-squares linear function approximation through the samples. |
490 | | * - Rescale all machine expressions to make their total time match, as we only care about |
491 | | * relative cost of each operation. |
492 | | * - Take the per-operation average of operation expressions across all machines, to construct |
493 | | * expressions for an average machine. |
494 | | * - Approximate the result with integer coefficients. Each cost unit corresponds to somewhere |
495 | | * between 0.5 ns and 2.5 ns, depending on the hardware. |
496 | | */ |
497 | | class SFLDefaultCostModel |
498 | | { |
499 | | uint64_t m_cost{0}; |
500 | | |
501 | | public: |
502 | 1.32M | inline void InitializeBegin() noexcept {} |
503 | | inline void InitializeEnd(int num_txns, int num_deps) noexcept |
504 | 1.32M | { |
505 | | // Cost of initialization. |
506 | 1.32M | m_cost += 39 * num_txns; |
507 | | // Cost of producing linearization at the end. |
508 | 1.32M | m_cost += 48 * num_txns + 4 * num_deps; |
509 | 1.32M | } |
510 | 1.32M | inline void GetLinearizationBegin() noexcept {} |
511 | | inline void GetLinearizationEnd(int num_txns, int num_deps) noexcept |
512 | 1.32M | { |
513 | | // Note that we account for the cost of the final linearization at the beginning (see |
514 | | // InitializeEnd), because the cost budget decision needs to be made before calling |
515 | | // GetLinearization. |
516 | | // This function exists here to allow overriding it easily for benchmark purposes. |
517 | 1.32M | } |
518 | 773k | inline void MakeTopologicalBegin() noexcept {} |
519 | | inline void MakeTopologicalEnd(int num_chunks, int num_steps) noexcept |
520 | 773k | { |
521 | 773k | m_cost += 20 * num_chunks + 28 * num_steps; |
522 | 773k | } |
523 | 1.30M | inline void StartOptimizingBegin() noexcept {} |
524 | 1.30M | inline void StartOptimizingEnd(int num_chunks) noexcept { m_cost += 13 * num_chunks; } |
525 | 2.96M | inline void ActivateBegin() noexcept {} |
526 | 2.96M | inline void ActivateEnd(int num_deps) noexcept { m_cost += 10 * num_deps + 1; } |
527 | 801k | inline void DeactivateBegin() noexcept {} |
528 | 801k | inline void DeactivateEnd(int num_deps) noexcept { m_cost += 11 * num_deps + 8; } |
529 | 2.96M | inline void MergeChunksBegin() noexcept {} |
530 | 2.96M | inline void MergeChunksMid(int num_txns) noexcept { m_cost += 2 * num_txns; } |
531 | 2.96M | inline void MergeChunksEnd(int num_steps) noexcept { m_cost += 3 * num_steps + 5; } |
532 | 12.2M | inline void PickMergeCandidateBegin() noexcept {} |
533 | 12.2M | inline void PickMergeCandidateEnd(int num_steps) noexcept { m_cost += 8 * num_steps; } |
534 | 3.94M | inline void PickChunkToOptimizeBegin() noexcept {} |
535 | 3.94M | inline void PickChunkToOptimizeEnd(int num_steps) noexcept { m_cost += num_steps + 4; } |
536 | 3.94M | inline void PickDependencyToSplitBegin() noexcept {} |
537 | 3.94M | inline void PickDependencyToSplitEnd(int num_txns) noexcept { m_cost += 8 * num_txns + 9; } |
538 | 1.30M | inline void StartMinimizingBegin() noexcept {} |
539 | 1.30M | inline void StartMinimizingEnd(int num_chunks) noexcept { m_cost += 18 * num_chunks; } |
540 | 5.36M | inline void MinimizeStepBegin() noexcept {} |
541 | 5.36M | inline void MinimizeStepMid(int num_txns) noexcept { m_cost += 11 * num_txns + 11; } |
542 | 672k | inline void MinimizeStepEnd(bool split) noexcept { m_cost += 17 * split + 7; } |
543 | | |
544 | 11.9M | inline uint64_t GetCost() const noexcept { return m_cost; } |
545 | | }; |
546 | | |
547 | | /** Class to represent the internal state of the spanning-forest linearization (SFL) algorithm. |
548 | | * |
549 | | * At all times, each dependency is marked as either "active" or "inactive". The subset of active |
550 | | * dependencies is the state of the SFL algorithm. The implementation maintains several other |
551 | | * values to speed up operations, but everything is ultimately a function of what that subset of |
552 | | * active dependencies is. |
553 | | * |
554 | | * Given such a subset, define a chunk as the set of transactions that are connected through active |
555 | | * dependencies (ignoring their parent/child direction). Thus, every state implies a particular |
556 | | * partitioning of the graph into chunks (including potential singletons). In the extreme, each |
557 | | * transaction may be in its own chunk, or in the other extreme all transactions may form a single |
558 | | * chunk. A chunk's feerate is its total fee divided by its total size. |
559 | | * |
560 | | * The algorithm consists of switching dependencies between active and inactive. The final |
561 | | * linearization that is produced at the end consists of these chunks, sorted from high to low |
562 | | * feerate, each individually sorted in an arbitrary but topological (= no child before parent) |
563 | | * way. |
564 | | * |
565 | | * We define four quality properties the state can have: |
566 | | * |
567 | | * - acyclic: The state is acyclic whenever no cycle of active dependencies exists within the |
568 | | * graph, ignoring the parent/child direction. This is equivalent to saying that within |
569 | | * each chunk the set of active dependencies form a tree, and thus the overall set of |
570 | | * active dependencies in the graph form a spanning forest, giving the algorithm its |
571 | | * name. Being acyclic is also equivalent to every chunk of N transactions having |
572 | | * exactly N-1 active dependencies. |
573 | | * |
574 | | * For example in a diamond graph, D->{B,C}->A, the 4 dependencies cannot be |
575 | | * simultaneously active. If at least one is inactive, the state is acyclic. |
576 | | * |
577 | | * The algorithm maintains an acyclic state at *all* times as an invariant. This implies |
578 | | * that activating a dependency always corresponds to merging two chunks, and that |
579 | | * deactivating one always corresponds to splitting two chunks. |
580 | | * |
581 | | * - topological: We say the state is topological whenever it is acyclic and no inactive dependency |
582 | | * exists between two distinct chunks such that the child chunk has higher or equal |
583 | | * feerate than the parent chunk. |
584 | | * |
585 | | * The relevance is that whenever the state is topological, the produced output |
586 | | * linearization will be topological too (i.e., not have children before parents). |
587 | | * Note that the "or equal" part of the definition matters: if not, one can end up |
588 | | * in a situation with mutually-dependent equal-feerate chunks that cannot be |
589 | | * linearized. For example C->{A,B} and D->{A,B}, with C->A and D->B active. The AC |
590 | | * chunk depends on DB through C->B, and the BD chunk depends on AC through D->A. |
591 | | * Merging them into a single ABCD chunk fixes this. |
592 | | * |
593 | | * The algorithm attempts to keep the state topological as much as possible, so it |
594 | | * can be interrupted to produce an output whenever, but will sometimes need to |
595 | | * temporarily deviate from it when improving the state. |
596 | | * |
597 | | * - optimal: For every active dependency, define its top and bottom set as the set of transactions |
598 | | * in the chunks that would result if the dependency were deactivated; the top being the |
599 | | * one with the dependency's parent, and the bottom being the one with the child. Note |
600 | | * that due to acyclicity, every deactivation splits a chunk exactly in two. |
601 | | * |
602 | | * We say the state is optimal whenever it is topological and it has no active |
603 | | * dependency whose top feerate is strictly higher than its bottom feerate. The |
604 | | * relevance is that it can be proven that whenever the state is optimal, the produced |
605 | | * linearization will also be optimal (in the convexified feerate diagram sense). It can |
606 | | * also be proven that for every graph at least one optimal state exists. |
607 | | * |
608 | | * Note that it is possible for the SFL state to not be optimal, but the produced |
609 | | * linearization to still be optimal. This happens when the chunks of a state are |
610 | | * identical to those of an optimal state, but the exact set of active dependencies |
611 | | * within a chunk differ in such a way that the state optimality condition is not |
612 | | * satisfied. Thus, the state being optimal is more a "the eventual output is *known* |
613 | | * to be optimal". |
614 | | * |
615 | | * - minimal: We say the state is minimal when it is: |
616 | | * - acyclic |
617 | | * - topological, except that inactive dependencies between equal-feerate chunks are |
618 | | * allowed as long as they do not form a loop. |
619 | | * - like optimal, no active dependencies whose top feerate is strictly higher than |
620 | | * the bottom feerate are allowed. |
621 | | * - no chunk contains a proper non-empty subset which includes all its own in-chunk |
622 | | * dependencies of the same feerate as the chunk itself. |
623 | | * |
624 | | * A minimal state effectively corresponds to an optimal state, where every chunk has |
625 | | * been split into its minimal equal-feerate components. |
626 | | * |
627 | | * The algorithm terminates whenever a minimal state is reached. |
628 | | * |
629 | | * |
630 | | * This leads to the following high-level algorithm: |
631 | | * - Start with all dependencies inactive, and thus all transactions in their own chunk. This is |
632 | | * definitely acyclic. |
633 | | * - Activate dependencies (merging chunks) until the state is topological. |
634 | | * - Loop until optimal (no dependencies with higher-feerate top than bottom), or time runs out: |
635 | | * - Deactivate a violating dependency, potentially making the state non-topological. |
636 | | * - Activate other dependencies to make the state topological again. |
637 | | * - If there is time left and the state is optimal: |
638 | | * - Attempt to split chunks into equal-feerate parts without mutual dependencies between them. |
639 | | * When this succeeds, recurse into them. |
640 | | * - If no such chunks can be found, the state is minimal. |
641 | | * - Output the chunks from high to low feerate, each internally sorted topologically. |
642 | | * |
643 | | * When merging, we always either: |
644 | | * - Merge upwards: merge a chunk with the lowest-feerate other chunk it depends on, among those |
645 | | * with lower or equal feerate than itself. |
646 | | * - Merge downwards: merge a chunk with the highest-feerate other chunk that depends on it, among |
647 | | * those with higher or equal feerate than itself. |
648 | | * |
649 | | * Using these strategies in the improvement loop above guarantees that the output linearization |
650 | | * after a deactivate + merge step is never worse or incomparable (in the convexified feerate |
651 | | * diagram sense) than the output linearization that would be produced before the step. With that, |
652 | | * we can refine the high-level algorithm to: |
653 | | * - Start with all dependencies inactive. |
654 | | * - Perform merges as described until none are possible anymore, making the state topological. |
655 | | * - Loop until optimal or time runs out: |
656 | | * - Pick a dependency D to deactivate among those with higher feerate top than bottom. |
657 | | * - Deactivate D, causing the chunk it is in to split into top T and bottom B. |
658 | | * - Do an upwards merge of T, if possible. If so, repeat the same with the merged result. |
659 | | * - Do a downwards merge of B, if possible. If so, repeat the same with the merged result. |
660 | | * - Split chunks further to obtain a minimal state, see below. |
661 | | * - Output the chunks from high to low feerate, each internally sorted topologically. |
662 | | * |
663 | | * Instead of performing merges arbitrarily to make the initial state topological, it is possible |
664 | | * to do so guided by an existing linearization. This has the advantage that the state's would-be |
665 | | * output linearization is immediately as good as the existing linearization it was based on: |
666 | | * - Start with all dependencies inactive. |
667 | | * - For each transaction t in the existing linearization: |
668 | | * - Find the chunk C that transaction is in (which will be singleton). |
669 | | * - Do an upwards merge of C, if possible. If so, repeat the same with the merged result. |
670 | | * No downwards merges are needed in this case. |
671 | | * |
672 | | * After reaching an optimal state, it can be transformed into a minimal state by attempting to |
673 | | * split chunks further into equal-feerate parts. To do so, pick a specific transaction in each |
674 | | * chunk (the pivot), and rerun the above split-then-merge procedure again: |
675 | | * - first, while pretending the pivot transaction has an infinitesimally higher (or lower) fee |
676 | | * than it really has. If a split exists with the pivot in the top part (or bottom part), this |
677 | | * will find it. |
678 | | * - if that fails to split, repeat while pretending the pivot transaction has an infinitesimally |
679 | | * lower (or higher) fee. If a split exists with the pivot in the bottom part (or top part), this |
680 | | * will find it. |
681 | | * - if either succeeds, repeat the procedure for the newly found chunks to split them further. |
682 | | * If not, the chunk is already minimal. |
683 | | * If the chunk can be split into equal-feerate parts, then the pivot must exist in either the top |
684 | | * or bottom part of that potential split. By trying both with the same pivot, if a split exists, |
685 | | * it will be found. |
686 | | * |
687 | | * What remains to be specified are a number of heuristics: |
688 | | * |
689 | | * - How to decide which chunks to merge: |
690 | | * - The merge upwards and downward rules specify that the lowest-feerate respectively |
691 | | * highest-feerate candidate chunk is merged with, but if there are multiple equal-feerate |
692 | | * candidates, a uniformly random one among them is picked. |
693 | | * |
694 | | * - How to decide what dependency to activate (when merging chunks): |
695 | | * - After picking two chunks to be merged (see above), a uniformly random dependency between the |
696 | | * two chunks is activated. |
697 | | * |
698 | | * - How to decide which chunk to find a dependency to split in: |
699 | | * - A round-robin queue of chunks to improve is maintained. The initial ordering of this queue |
700 | | * is uniformly randomly permuted. |
701 | | * |
702 | | * - How to decide what dependency to deactivate (when splitting chunks): |
703 | | * - Inside the selected chunk (see above), among the dependencies whose top feerate is strictly |
704 | | * higher than its bottom feerate in the selected chunk, if any, a uniformly random dependency |
705 | | * is deactivated. |
706 | | * - After every split, it is possible that the top and the bottom chunk merge with each other |
707 | | * again in the merge sequence (through a top->bottom dependency, not through the deactivated |
708 | | * one, which was bottom->top). Call this a self-merge. If a self-merge does not occur after |
709 | | * a split, the resulting linearization is strictly improved (the area under the convexified |
710 | | * feerate diagram increases by at least gain/2), while self-merges do not change it. |
711 | | * |
712 | | * - How to decide the exact output linearization: |
713 | | * - When there are multiple equal-feerate chunks with no dependencies between them, pick the |
714 | | * smallest one first. If there are multiple smallest ones, pick the one that contains the |
715 | | * last transaction (according to the provided fallback order) last (note that this is not the |
716 | | * same as picking the chunk with the first transaction first). |
717 | | * - Within chunks, pick among all transactions without missing dependencies the one with the |
718 | | * highest individual feerate. If there are multiple ones with the same individual feerate, |
719 | | * pick the smallest first. If there are multiple with the same fee and size, pick the one |
720 | | * that sorts first according to the fallback order first. |
721 | | */ |
722 | | template<typename SetType, typename CostModel = SFLDefaultCostModel> |
723 | | class SpanningForestState |
724 | | { |
725 | | private: |
726 | | /** Internal RNG. */ |
727 | | InsecureRandomContext m_rng; |
728 | | |
729 | | /** Data type to represent indexing into m_tx_data. */ |
730 | | using TxIdx = DepGraphIndex; |
731 | | /** Data type to represent indexing into m_set_info. Use the smallest type possible to improve |
732 | | * cache locality. */ |
733 | | using SetIdx = std::conditional_t<(SetType::Size() <= 0xff), |
734 | | uint8_t, |
735 | | std::conditional_t<(SetType::Size() <= 0xffff), |
736 | | uint16_t, |
737 | | uint32_t>>; |
738 | | /** An invalid SetIdx. */ |
739 | | static constexpr SetIdx INVALID_SET_IDX = SetIdx(-1); |
740 | | |
741 | | /** Structure with information about a single transaction. */ |
742 | | struct TxData { |
743 | | /** The top set for every active child dependency this transaction has, indexed by child |
744 | | * TxIdx. Only defined for indexes in active_children. */ |
745 | | std::array<SetIdx, SetType::Size()> dep_top_idx; |
746 | | /** The set of parent transactions of this transaction. Immutable after construction. */ |
747 | | SetType parents; |
748 | | /** The set of child transactions of this transaction. Immutable after construction. */ |
749 | | SetType children; |
750 | | /** The set of child transactions reachable through an active dependency. */ |
751 | | SetType active_children; |
752 | | /** Which chunk this transaction belongs to. */ |
753 | | SetIdx chunk_idx; |
754 | | }; |
755 | | |
756 | | /** The set of all TxIdx's of transactions in the cluster indexing into m_tx_data. */ |
757 | | SetType m_transaction_idxs; |
758 | | /** The set of all chunk SetIdx's. This excludes the SetIdxs that refer to active |
759 | | * dependencies' tops. */ |
760 | | SetType m_chunk_idxs; |
761 | | /** The set of all SetIdx's that appear in m_suboptimal_chunks. Note that they do not need to |
762 | | * be chunks: some of these sets may have been converted to a dependency's top set since being |
763 | | * added to m_suboptimal_chunks. */ |
764 | | SetType m_suboptimal_idxs; |
765 | | /** Information about each transaction (and chunks). Keeps the "holes" from DepGraph during |
766 | | * construction. Indexed by TxIdx. */ |
767 | | std::vector<TxData> m_tx_data; |
768 | | /** Information about each set (chunk, or active dependency top set). Indexed by SetIdx. */ |
769 | | std::vector<SetInfo<SetType>> m_set_info; |
770 | | /** For each chunk, indexed by SetIdx, the set of out-of-chunk reachable transactions, in the |
771 | | * upwards (.first) and downwards (.second) direction. */ |
772 | | std::vector<std::pair<SetType, SetType>> m_reachable; |
773 | | /** A FIFO of chunk SetIdxs for chunks that may be improved still. */ |
774 | | VecDeque<SetIdx> m_suboptimal_chunks; |
775 | | /** A FIFO of chunk indexes with a pivot transaction in them, and a flag to indicate their |
776 | | * status: |
777 | | * - bit 1: currently attempting to move the pivot down, rather than up. |
778 | | * - bit 2: this is the second stage, so we have already tried moving the pivot in the other |
779 | | * direction. |
780 | | */ |
781 | | VecDeque<std::tuple<SetIdx, TxIdx, unsigned>> m_nonminimal_chunks; |
782 | | |
783 | | /** The DepGraph we are trying to linearize. */ |
784 | | const DepGraph<SetType>& m_depgraph; |
785 | | |
786 | | /** Accounting for the cost of this computation. */ |
787 | | CostModel m_cost; |
788 | | |
789 | | /** Pick a random transaction within a set (which must be non-empty). */ |
790 | | TxIdx PickRandomTx(const SetType& tx_idxs) noexcept |
791 | 4.44M | { |
792 | 4.44M | Assume(tx_idxs.Any()); |
793 | 4.44M | unsigned pos = m_rng.randrange<unsigned>(tx_idxs.Count()); |
794 | 5.84M | for (auto tx_idx : tx_idxs) { Branch (794:26): [True: 27.3k, False: 0]
Branch (794:26): [True: 22.9k, False: 0]
Branch (794:26): [True: 5.79M, False: 0]
|
795 | 5.84M | if (pos == 0) return tx_idx; Branch (795:17): [True: 14.4k, False: 12.8k]
Branch (795:17): [True: 17.8k, False: 5.09k]
Branch (795:17): [True: 4.41M, False: 1.37M]
|
796 | 1.39M | --pos; |
797 | 1.39M | } |
798 | 0 | Assume(false); |
799 | 0 | return TxIdx(-1); |
800 | 4.44M | } _ZN17cluster_linearize19SpanningForestStateIN13bitset_detail9IntBitSetIjEENS_19SFLDefaultCostModelEE12PickRandomTxERKS3_ Line | Count | Source | 791 | 14.4k | { | 792 | 14.4k | Assume(tx_idxs.Any()); | 793 | 14.4k | unsigned pos = m_rng.randrange<unsigned>(tx_idxs.Count()); | 794 | 27.3k | for (auto tx_idx : tx_idxs) { Branch (794:26): [True: 27.3k, False: 0]
| 795 | 27.3k | if (pos == 0) return tx_idx; Branch (795:17): [True: 14.4k, False: 12.8k]
| 796 | 12.8k | --pos; | 797 | 12.8k | } | 798 | 0 | Assume(false); | 799 | 0 | return TxIdx(-1); | 800 | 14.4k | } |
_ZN17cluster_linearize19SpanningForestStateIN13bitset_detail14MultiIntBitSetImLj2EEENS_19SFLDefaultCostModelEE12PickRandomTxERKS3_ Line | Count | Source | 791 | 17.8k | { | 792 | 17.8k | Assume(tx_idxs.Any()); | 793 | 17.8k | unsigned pos = m_rng.randrange<unsigned>(tx_idxs.Count()); | 794 | 22.9k | for (auto tx_idx : tx_idxs) { Branch (794:26): [True: 22.9k, False: 0]
| 795 | 22.9k | if (pos == 0) return tx_idx; Branch (795:17): [True: 17.8k, False: 5.09k]
| 796 | 5.09k | --pos; | 797 | 5.09k | } | 798 | 0 | Assume(false); | 799 | 0 | return TxIdx(-1); | 800 | 17.8k | } |
_ZN17cluster_linearize19SpanningForestStateIN13bitset_detail9IntBitSetImEENS_19SFLDefaultCostModelEE12PickRandomTxERKS3_ Line | Count | Source | 791 | 4.41M | { | 792 | 4.41M | Assume(tx_idxs.Any()); | 793 | 4.41M | unsigned pos = m_rng.randrange<unsigned>(tx_idxs.Count()); | 794 | 5.79M | for (auto tx_idx : tx_idxs) { Branch (794:26): [True: 5.79M, False: 0]
| 795 | 5.79M | if (pos == 0) return tx_idx; Branch (795:17): [True: 4.41M, False: 1.37M]
| 796 | 1.37M | --pos; | 797 | 1.37M | } | 798 | 0 | Assume(false); | 799 | 0 | return TxIdx(-1); | 800 | 4.41M | } |
|
801 | | |
802 | | /** Find the set of out-of-chunk transactions reachable from tx_idxs, both in upwards and |
803 | | * downwards direction. Only used by SanityCheck to verify the precomputed reachable sets in |
804 | | * m_reachable that are maintained by Activate/Deactivate. */ |
805 | | std::pair<SetType, SetType> GetReachable(const SetType& tx_idxs) const noexcept |
806 | 27.5k | { |
807 | 27.5k | SetType parents, children; |
808 | 49.1k | for (auto tx_idx : tx_idxs) { Branch (808:26): [True: 49.1k, False: 27.5k]
|
809 | 49.1k | const auto& tx_data = m_tx_data[tx_idx]; |
810 | 49.1k | parents |= tx_data.parents; |
811 | 49.1k | children |= tx_data.children; |
812 | 49.1k | } |
813 | 27.5k | return {parents - tx_idxs, children - tx_idxs}; |
814 | 27.5k | } |
815 | | |
816 | | /** Make the inactive dependency from child to parent, which must not be in the same chunk |
817 | | * already, active. Returns the merged chunk idx. */ |
818 | | SetIdx Activate(TxIdx parent_idx, TxIdx child_idx) noexcept |
819 | 2.96M | { |
820 | 2.96M | m_cost.ActivateBegin(); |
821 | | // Gather and check information about the parent and child transactions. |
822 | 2.96M | auto& parent_data = m_tx_data[parent_idx]; |
823 | 2.96M | auto& child_data = m_tx_data[child_idx]; |
824 | 2.96M | Assume(parent_data.children[child_idx]); |
825 | 2.96M | Assume(!parent_data.active_children[child_idx]); |
826 | | // Get the set index of the chunks the parent and child are currently in. The parent chunk |
827 | | // will become the top set of the newly activated dependency, while the child chunk will be |
828 | | // grown to become the merged chunk. |
829 | 2.96M | auto parent_chunk_idx = parent_data.chunk_idx; |
830 | 2.96M | auto child_chunk_idx = child_data.chunk_idx; |
831 | 2.96M | Assume(parent_chunk_idx != child_chunk_idx); |
832 | 2.96M | Assume(m_chunk_idxs[parent_chunk_idx]); |
833 | 2.96M | Assume(m_chunk_idxs[child_chunk_idx]); |
834 | 2.96M | auto& top_info = m_set_info[parent_chunk_idx]; |
835 | 2.96M | auto& bottom_info = m_set_info[child_chunk_idx]; |
836 | | |
837 | | // Consider the following example: |
838 | | // |
839 | | // A A There are two chunks, ABC and DEF, and the inactive E->C dependency |
840 | | // / \ / \ is activated, resulting in a single chunk ABCDEF. |
841 | | // B C B C |
842 | | // : ==> | Dependency | top set before | top set after | change |
843 | | // D E D E B->A | AC | ACDEF | +DEF |
844 | | // \ / \ / C->A | AB | AB | |
845 | | // F F F->D | D | D | |
846 | | // F->E | E | ABCE | +ABC |
847 | | // |
848 | | // The common pattern here is that any dependency which has the parent or child of the |
849 | | // dependency being activated (E->C here) in its top set, will have the opposite part added |
850 | | // to it. This is true for B->A and F->E, but not for C->A and F->D. |
851 | | // |
852 | | // Traverse the old parent chunk top_info (ABC in example), and add bottom_info (DEF) to |
853 | | // every dependency's top set which has the parent (C) in it. At the same time, change the |
854 | | // chunk_idx for each to be child_chunk_idx, which becomes the set for the merged chunk. |
855 | 16.4M | for (auto tx_idx : top_info.transactions) { Branch (855:26): [True: 122k, False: 23.4k]
Branch (855:26): [True: 28.6k, False: 7.89k]
Branch (855:26): [True: 16.2M, False: 2.92M]
|
856 | 16.4M | auto& tx_data = m_tx_data[tx_idx]; |
857 | 16.4M | tx_data.chunk_idx = child_chunk_idx; |
858 | 16.4M | for (auto dep_child_idx : tx_data.active_children) { Branch (858:37): [True: 98.8k, False: 122k]
Branch (858:37): [True: 20.7k, False: 28.6k]
Branch (858:37): [True: 13.3M, False: 16.2M]
|
859 | 13.4M | auto& dep_top_info = m_set_info[tx_data.dep_top_idx[dep_child_idx]]; |
860 | 13.4M | if (dep_top_info.transactions[parent_idx]) dep_top_info |= bottom_info; Branch (860:21): [True: 46.8k, False: 51.9k]
Branch (860:21): [True: 9.47k, False: 11.2k]
Branch (860:21): [True: 9.03M, False: 4.32M]
|
861 | 13.4M | } |
862 | 16.4M | } |
863 | | // Traverse the old child chunk bottom_info (DEF in example), and add top_info (ABC) to |
864 | | // every dependency's top set which has the child (E) in it. |
865 | 5.86M | for (auto tx_idx : bottom_info.transactions) { Branch (865:26): [True: 123k, False: 23.4k]
Branch (865:26): [True: 18.7k, False: 7.89k]
Branch (865:26): [True: 5.72M, False: 2.92M]
|
866 | 5.86M | auto& tx_data = m_tx_data[tx_idx]; |
867 | 5.86M | for (auto dep_child_idx : tx_data.active_children) { Branch (867:37): [True: 100k, False: 123k]
Branch (867:37): [True: 10.8k, False: 18.7k]
Branch (867:37): [True: 2.79M, False: 5.72M]
|
868 | 2.90M | auto& dep_top_info = m_set_info[tx_data.dep_top_idx[dep_child_idx]]; |
869 | 2.90M | if (dep_top_info.transactions[child_idx]) dep_top_info |= top_info; Branch (869:21): [True: 30.9k, False: 69.4k]
Branch (869:21): [True: 3.14k, False: 7.68k]
Branch (869:21): [True: 1.42M, False: 1.36M]
|
870 | 2.90M | } |
871 | 5.86M | } |
872 | | // Merge top_info into bottom_info, which becomes the merged chunk. |
873 | 2.96M | bottom_info |= top_info; |
874 | | // Compute merged sets of reachable transactions from the new chunk, based on the input |
875 | | // chunks' reachable sets. |
876 | 2.96M | m_reachable[child_chunk_idx].first |= m_reachable[parent_chunk_idx].first; |
877 | 2.96M | m_reachable[child_chunk_idx].second |= m_reachable[parent_chunk_idx].second; |
878 | 2.96M | m_reachable[child_chunk_idx].first -= bottom_info.transactions; |
879 | 2.96M | m_reachable[child_chunk_idx].second -= bottom_info.transactions; |
880 | | // Make parent chunk the set for the new active dependency. |
881 | 2.96M | parent_data.dep_top_idx[child_idx] = parent_chunk_idx; |
882 | 2.96M | parent_data.active_children.Set(child_idx); |
883 | 2.96M | m_chunk_idxs.Reset(parent_chunk_idx); |
884 | | // Return the newly merged chunk. |
885 | 2.96M | m_cost.ActivateEnd(/*num_deps=*/bottom_info.transactions.Count() - 1); |
886 | 2.96M | return child_chunk_idx; |
887 | 2.96M | } _ZN17cluster_linearize19SpanningForestStateIN13bitset_detail9IntBitSetIjEENS_19SFLDefaultCostModelEE8ActivateEjj Line | Count | Source | 819 | 23.4k | { | 820 | 23.4k | m_cost.ActivateBegin(); | 821 | | // Gather and check information about the parent and child transactions. | 822 | 23.4k | auto& parent_data = m_tx_data[parent_idx]; | 823 | 23.4k | auto& child_data = m_tx_data[child_idx]; | 824 | 23.4k | Assume(parent_data.children[child_idx]); | 825 | 23.4k | Assume(!parent_data.active_children[child_idx]); | 826 | | // Get the set index of the chunks the parent and child are currently in. The parent chunk | 827 | | // will become the top set of the newly activated dependency, while the child chunk will be | 828 | | // grown to become the merged chunk. | 829 | 23.4k | auto parent_chunk_idx = parent_data.chunk_idx; | 830 | 23.4k | auto child_chunk_idx = child_data.chunk_idx; | 831 | 23.4k | Assume(parent_chunk_idx != child_chunk_idx); | 832 | 23.4k | Assume(m_chunk_idxs[parent_chunk_idx]); | 833 | 23.4k | Assume(m_chunk_idxs[child_chunk_idx]); | 834 | 23.4k | auto& top_info = m_set_info[parent_chunk_idx]; | 835 | 23.4k | auto& bottom_info = m_set_info[child_chunk_idx]; | 836 | | | 837 | | // Consider the following example: | 838 | | // | 839 | | // A A There are two chunks, ABC and DEF, and the inactive E->C dependency | 840 | | // / \ / \ is activated, resulting in a single chunk ABCDEF. | 841 | | // B C B C | 842 | | // : ==> | Dependency | top set before | top set after | change | 843 | | // D E D E B->A | AC | ACDEF | +DEF | 844 | | // \ / \ / C->A | AB | AB | | 845 | | // F F F->D | D | D | | 846 | | // F->E | E | ABCE | +ABC | 847 | | // | 848 | | // The common pattern here is that any dependency which has the parent or child of the | 849 | | // dependency being activated (E->C here) in its top set, will have the opposite part added | 850 | | // to it. This is true for B->A and F->E, but not for C->A and F->D. | 851 | | // | 852 | | // Traverse the old parent chunk top_info (ABC in example), and add bottom_info (DEF) to | 853 | | // every dependency's top set which has the parent (C) in it. At the same time, change the | 854 | | // chunk_idx for each to be child_chunk_idx, which becomes the set for the merged chunk. | 855 | 122k | for (auto tx_idx : top_info.transactions) { Branch (855:26): [True: 122k, False: 23.4k]
| 856 | 122k | auto& tx_data = m_tx_data[tx_idx]; | 857 | 122k | tx_data.chunk_idx = child_chunk_idx; | 858 | 122k | for (auto dep_child_idx : tx_data.active_children) { Branch (858:37): [True: 98.8k, False: 122k]
| 859 | 98.8k | auto& dep_top_info = m_set_info[tx_data.dep_top_idx[dep_child_idx]]; | 860 | 98.8k | if (dep_top_info.transactions[parent_idx]) dep_top_info |= bottom_info; Branch (860:21): [True: 46.8k, False: 51.9k]
| 861 | 98.8k | } | 862 | 122k | } | 863 | | // Traverse the old child chunk bottom_info (DEF in example), and add top_info (ABC) to | 864 | | // every dependency's top set which has the child (E) in it. | 865 | 123k | for (auto tx_idx : bottom_info.transactions) { Branch (865:26): [True: 123k, False: 23.4k]
| 866 | 123k | auto& tx_data = m_tx_data[tx_idx]; | 867 | 123k | for (auto dep_child_idx : tx_data.active_children) { Branch (867:37): [True: 100k, False: 123k]
| 868 | 100k | auto& dep_top_info = m_set_info[tx_data.dep_top_idx[dep_child_idx]]; | 869 | 100k | if (dep_top_info.transactions[child_idx]) dep_top_info |= top_info; Branch (869:21): [True: 30.9k, False: 69.4k]
| 870 | 100k | } | 871 | 123k | } | 872 | | // Merge top_info into bottom_info, which becomes the merged chunk. | 873 | 23.4k | bottom_info |= top_info; | 874 | | // Compute merged sets of reachable transactions from the new chunk, based on the input | 875 | | // chunks' reachable sets. | 876 | 23.4k | m_reachable[child_chunk_idx].first |= m_reachable[parent_chunk_idx].first; | 877 | 23.4k | m_reachable[child_chunk_idx].second |= m_reachable[parent_chunk_idx].second; | 878 | 23.4k | m_reachable[child_chunk_idx].first -= bottom_info.transactions; | 879 | 23.4k | m_reachable[child_chunk_idx].second -= bottom_info.transactions; | 880 | | // Make parent chunk the set for the new active dependency. | 881 | 23.4k | parent_data.dep_top_idx[child_idx] = parent_chunk_idx; | 882 | 23.4k | parent_data.active_children.Set(child_idx); | 883 | 23.4k | m_chunk_idxs.Reset(parent_chunk_idx); | 884 | | // Return the newly merged chunk. | 885 | 23.4k | m_cost.ActivateEnd(/*num_deps=*/bottom_info.transactions.Count() - 1); | 886 | 23.4k | return child_chunk_idx; | 887 | 23.4k | } |
_ZN17cluster_linearize19SpanningForestStateIN13bitset_detail14MultiIntBitSetImLj2EEENS_19SFLDefaultCostModelEE8ActivateEjj Line | Count | Source | 819 | 7.89k | { | 820 | 7.89k | m_cost.ActivateBegin(); | 821 | | // Gather and check information about the parent and child transactions. | 822 | 7.89k | auto& parent_data = m_tx_data[parent_idx]; | 823 | 7.89k | auto& child_data = m_tx_data[child_idx]; | 824 | 7.89k | Assume(parent_data.children[child_idx]); | 825 | 7.89k | Assume(!parent_data.active_children[child_idx]); | 826 | | // Get the set index of the chunks the parent and child are currently in. The parent chunk | 827 | | // will become the top set of the newly activated dependency, while the child chunk will be | 828 | | // grown to become the merged chunk. | 829 | 7.89k | auto parent_chunk_idx = parent_data.chunk_idx; | 830 | 7.89k | auto child_chunk_idx = child_data.chunk_idx; | 831 | 7.89k | Assume(parent_chunk_idx != child_chunk_idx); | 832 | 7.89k | Assume(m_chunk_idxs[parent_chunk_idx]); | 833 | 7.89k | Assume(m_chunk_idxs[child_chunk_idx]); | 834 | 7.89k | auto& top_info = m_set_info[parent_chunk_idx]; | 835 | 7.89k | auto& bottom_info = m_set_info[child_chunk_idx]; | 836 | | | 837 | | // Consider the following example: | 838 | | // | 839 | | // A A There are two chunks, ABC and DEF, and the inactive E->C dependency | 840 | | // / \ / \ is activated, resulting in a single chunk ABCDEF. | 841 | | // B C B C | 842 | | // : ==> | Dependency | top set before | top set after | change | 843 | | // D E D E B->A | AC | ACDEF | +DEF | 844 | | // \ / \ / C->A | AB | AB | | 845 | | // F F F->D | D | D | | 846 | | // F->E | E | ABCE | +ABC | 847 | | // | 848 | | // The common pattern here is that any dependency which has the parent or child of the | 849 | | // dependency being activated (E->C here) in its top set, will have the opposite part added | 850 | | // to it. This is true for B->A and F->E, but not for C->A and F->D. | 851 | | // | 852 | | // Traverse the old parent chunk top_info (ABC in example), and add bottom_info (DEF) to | 853 | | // every dependency's top set which has the parent (C) in it. At the same time, change the | 854 | | // chunk_idx for each to be child_chunk_idx, which becomes the set for the merged chunk. | 855 | 28.6k | for (auto tx_idx : top_info.transactions) { Branch (855:26): [True: 28.6k, False: 7.89k]
| 856 | 28.6k | auto& tx_data = m_tx_data[tx_idx]; | 857 | 28.6k | tx_data.chunk_idx = child_chunk_idx; | 858 | 28.6k | for (auto dep_child_idx : tx_data.active_children) { Branch (858:37): [True: 20.7k, False: 28.6k]
| 859 | 20.7k | auto& dep_top_info = m_set_info[tx_data.dep_top_idx[dep_child_idx]]; | 860 | 20.7k | if (dep_top_info.transactions[parent_idx]) dep_top_info |= bottom_info; Branch (860:21): [True: 9.47k, False: 11.2k]
| 861 | 20.7k | } | 862 | 28.6k | } | 863 | | // Traverse the old child chunk bottom_info (DEF in example), and add top_info (ABC) to | 864 | | // every dependency's top set which has the child (E) in it. | 865 | 18.7k | for (auto tx_idx : bottom_info.transactions) { Branch (865:26): [True: 18.7k, False: 7.89k]
| 866 | 18.7k | auto& tx_data = m_tx_data[tx_idx]; | 867 | 18.7k | for (auto dep_child_idx : tx_data.active_children) { Branch (867:37): [True: 10.8k, False: 18.7k]
| 868 | 10.8k | auto& dep_top_info = m_set_info[tx_data.dep_top_idx[dep_child_idx]]; | 869 | 10.8k | if (dep_top_info.transactions[child_idx]) dep_top_info |= top_info; Branch (869:21): [True: 3.14k, False: 7.68k]
| 870 | 10.8k | } | 871 | 18.7k | } | 872 | | // Merge top_info into bottom_info, which becomes the merged chunk. | 873 | 7.89k | bottom_info |= top_info; | 874 | | // Compute merged sets of reachable transactions from the new chunk, based on the input | 875 | | // chunks' reachable sets. | 876 | 7.89k | m_reachable[child_chunk_idx].first |= m_reachable[parent_chunk_idx].first; | 877 | 7.89k | m_reachable[child_chunk_idx].second |= m_reachable[parent_chunk_idx].second; | 878 | 7.89k | m_reachable[child_chunk_idx].first -= bottom_info.transactions; | 879 | 7.89k | m_reachable[child_chunk_idx].second -= bottom_info.transactions; | 880 | | // Make parent chunk the set for the new active dependency. | 881 | 7.89k | parent_data.dep_top_idx[child_idx] = parent_chunk_idx; | 882 | 7.89k | parent_data.active_children.Set(child_idx); | 883 | 7.89k | m_chunk_idxs.Reset(parent_chunk_idx); | 884 | | // Return the newly merged chunk. | 885 | 7.89k | m_cost.ActivateEnd(/*num_deps=*/bottom_info.transactions.Count() - 1); | 886 | 7.89k | return child_chunk_idx; | 887 | 7.89k | } |
_ZN17cluster_linearize19SpanningForestStateIN13bitset_detail9IntBitSetImEENS_19SFLDefaultCostModelEE8ActivateEjj Line | Count | Source | 819 | 2.92M | { | 820 | 2.92M | m_cost.ActivateBegin(); | 821 | | // Gather and check information about the parent and child transactions. | 822 | 2.92M | auto& parent_data = m_tx_data[parent_idx]; | 823 | 2.92M | auto& child_data = m_tx_data[child_idx]; | 824 | 2.92M | Assume(parent_data.children[child_idx]); | 825 | 2.92M | Assume(!parent_data.active_children[child_idx]); | 826 | | // Get the set index of the chunks the parent and child are currently in. The parent chunk | 827 | | // will become the top set of the newly activated dependency, while the child chunk will be | 828 | | // grown to become the merged chunk. | 829 | 2.92M | auto parent_chunk_idx = parent_data.chunk_idx; | 830 | 2.92M | auto child_chunk_idx = child_data.chunk_idx; | 831 | 2.92M | Assume(parent_chunk_idx != child_chunk_idx); | 832 | 2.92M | Assume(m_chunk_idxs[parent_chunk_idx]); | 833 | 2.92M | Assume(m_chunk_idxs[child_chunk_idx]); | 834 | 2.92M | auto& top_info = m_set_info[parent_chunk_idx]; | 835 | 2.92M | auto& bottom_info = m_set_info[child_chunk_idx]; | 836 | | | 837 | | // Consider the following example: | 838 | | // | 839 | | // A A There are two chunks, ABC and DEF, and the inactive E->C dependency | 840 | | // / \ / \ is activated, resulting in a single chunk ABCDEF. | 841 | | // B C B C | 842 | | // : ==> | Dependency | top set before | top set after | change | 843 | | // D E D E B->A | AC | ACDEF | +DEF | 844 | | // \ / \ / C->A | AB | AB | | 845 | | // F F F->D | D | D | | 846 | | // F->E | E | ABCE | +ABC | 847 | | // | 848 | | // The common pattern here is that any dependency which has the parent or child of the | 849 | | // dependency being activated (E->C here) in its top set, will have the opposite part added | 850 | | // to it. This is true for B->A and F->E, but not for C->A and F->D. | 851 | | // | 852 | | // Traverse the old parent chunk top_info (ABC in example), and add bottom_info (DEF) to | 853 | | // every dependency's top set which has the parent (C) in it. At the same time, change the | 854 | | // chunk_idx for each to be child_chunk_idx, which becomes the set for the merged chunk. | 855 | 16.2M | for (auto tx_idx : top_info.transactions) { Branch (855:26): [True: 16.2M, False: 2.92M]
| 856 | 16.2M | auto& tx_data = m_tx_data[tx_idx]; | 857 | 16.2M | tx_data.chunk_idx = child_chunk_idx; | 858 | 16.2M | for (auto dep_child_idx : tx_data.active_children) { Branch (858:37): [True: 13.3M, False: 16.2M]
| 859 | 13.3M | auto& dep_top_info = m_set_info[tx_data.dep_top_idx[dep_child_idx]]; | 860 | 13.3M | if (dep_top_info.transactions[parent_idx]) dep_top_info |= bottom_info; Branch (860:21): [True: 9.03M, False: 4.32M]
| 861 | 13.3M | } | 862 | 16.2M | } | 863 | | // Traverse the old child chunk bottom_info (DEF in example), and add top_info (ABC) to | 864 | | // every dependency's top set which has the child (E) in it. | 865 | 5.72M | for (auto tx_idx : bottom_info.transactions) { Branch (865:26): [True: 5.72M, False: 2.92M]
| 866 | 5.72M | auto& tx_data = m_tx_data[tx_idx]; | 867 | 5.72M | for (auto dep_child_idx : tx_data.active_children) { Branch (867:37): [True: 2.79M, False: 5.72M]
| 868 | 2.79M | auto& dep_top_info = m_set_info[tx_data.dep_top_idx[dep_child_idx]]; | 869 | 2.79M | if (dep_top_info.transactions[child_idx]) dep_top_info |= top_info; Branch (869:21): [True: 1.42M, False: 1.36M]
| 870 | 2.79M | } | 871 | 5.72M | } | 872 | | // Merge top_info into bottom_info, which becomes the merged chunk. | 873 | 2.92M | bottom_info |= top_info; | 874 | | // Compute merged sets of reachable transactions from the new chunk, based on the input | 875 | | // chunks' reachable sets. | 876 | 2.92M | m_reachable[child_chunk_idx].first |= m_reachable[parent_chunk_idx].first; | 877 | 2.92M | m_reachable[child_chunk_idx].second |= m_reachable[parent_chunk_idx].second; | 878 | 2.92M | m_reachable[child_chunk_idx].first -= bottom_info.transactions; | 879 | 2.92M | m_reachable[child_chunk_idx].second -= bottom_info.transactions; | 880 | | // Make parent chunk the set for the new active dependency. | 881 | 2.92M | parent_data.dep_top_idx[child_idx] = parent_chunk_idx; | 882 | 2.92M | parent_data.active_children.Set(child_idx); | 883 | 2.92M | m_chunk_idxs.Reset(parent_chunk_idx); | 884 | | // Return the newly merged chunk. | 885 | 2.92M | m_cost.ActivateEnd(/*num_deps=*/bottom_info.transactions.Count() - 1); | 886 | 2.92M | return child_chunk_idx; | 887 | 2.92M | } |
|
888 | | |
889 | | /** Make a specified active dependency inactive. Returns the created parent and child chunk |
890 | | * indexes. */ |
891 | | std::pair<SetIdx, SetIdx> Deactivate(TxIdx parent_idx, TxIdx child_idx) noexcept |
892 | 801k | { |
893 | 801k | m_cost.DeactivateBegin(); |
894 | | // Gather and check information about the parent transactions. |
895 | 801k | auto& parent_data = m_tx_data[parent_idx]; |
896 | 801k | Assume(parent_data.children[child_idx]); |
897 | 801k | Assume(parent_data.active_children[child_idx]); |
898 | | // Get the top set of the active dependency (which will become the parent chunk) and the |
899 | | // chunk set the transactions are currently in (which will become the bottom chunk). |
900 | 801k | auto parent_chunk_idx = parent_data.dep_top_idx[child_idx]; |
901 | 801k | auto child_chunk_idx = parent_data.chunk_idx; |
902 | 801k | Assume(parent_chunk_idx != child_chunk_idx); |
903 | 801k | Assume(m_chunk_idxs[child_chunk_idx]); |
904 | 801k | Assume(!m_chunk_idxs[parent_chunk_idx]); // top set, not a chunk |
905 | 801k | auto& top_info = m_set_info[parent_chunk_idx]; |
906 | 801k | auto& bottom_info = m_set_info[child_chunk_idx]; |
907 | | |
908 | | // Remove the active dependency. |
909 | 801k | parent_data.active_children.Reset(child_idx); |
910 | 801k | m_chunk_idxs.Set(parent_chunk_idx); |
911 | 801k | auto ntx = bottom_info.transactions.Count(); |
912 | | // Subtract the top_info from the bottom_info, as it will become the child chunk. |
913 | 801k | bottom_info -= top_info; |
914 | | // See the comment above in Activate(). We perform the opposite operations here, removing |
915 | | // instead of adding. Simultaneously, aggregate the top/bottom's union of parents/children. |
916 | 801k | SetType top_parents, top_children; |
917 | 5.03M | for (auto tx_idx : top_info.transactions) { Branch (917:26): [True: 69.8k, False: 10.7k]
Branch (917:26): [True: 14.9k, False: 4.55k]
Branch (917:26): [True: 4.95M, False: 786k]
|
918 | 5.03M | auto& tx_data = m_tx_data[tx_idx]; |
919 | 5.03M | tx_data.chunk_idx = parent_chunk_idx; |
920 | 5.03M | top_parents |= tx_data.parents; |
921 | 5.03M | top_children |= tx_data.children; |
922 | 5.03M | for (auto dep_child_idx : tx_data.active_children) { Branch (922:37): [True: 59.0k, False: 69.8k]
Branch (922:37): [True: 10.4k, False: 14.9k]
Branch (922:37): [True: 4.16M, False: 4.95M]
|
923 | 4.23M | auto& dep_top_info = m_set_info[tx_data.dep_top_idx[dep_child_idx]]; |
924 | 4.23M | if (dep_top_info.transactions[parent_idx]) dep_top_info -= bottom_info; Branch (924:21): [True: 25.0k, False: 33.9k]
Branch (924:21): [True: 5.04k, False: 5.39k]
Branch (924:21): [True: 3.40M, False: 763k]
|
925 | 4.23M | } |
926 | 5.03M | } |
927 | 801k | SetType bottom_parents, bottom_children; |
928 | 1.69M | for (auto tx_idx : bottom_info.transactions) { Branch (928:26): [True: 66.5k, False: 10.7k]
Branch (928:26): [True: 11.7k, False: 4.55k]
Branch (928:26): [True: 1.62M, False: 786k]
|
929 | 1.69M | auto& tx_data = m_tx_data[tx_idx]; |
930 | 1.69M | bottom_parents |= tx_data.parents; |
931 | 1.69M | bottom_children |= tx_data.children; |
932 | 1.69M | for (auto dep_child_idx : tx_data.active_children) { Branch (932:37): [True: 55.7k, False: 66.5k]
Branch (932:37): [True: 7.22k, False: 11.7k]
Branch (932:37): [True: 834k, False: 1.62M]
|
933 | 897k | auto& dep_top_info = m_set_info[tx_data.dep_top_idx[dep_child_idx]]; |
934 | 897k | if (dep_top_info.transactions[child_idx]) dep_top_info -= top_info; Branch (934:21): [True: 24.3k, False: 31.4k]
Branch (934:21): [True: 3.86k, False: 3.35k]
Branch (934:21): [True: 545k, False: 288k]
|
935 | 897k | } |
936 | 1.69M | } |
937 | | // Compute the new sets of reachable transactions for each new chunk, based on the |
938 | | // top/bottom parents and children computed above. |
939 | 801k | m_reachable[parent_chunk_idx].first = top_parents - top_info.transactions; |
940 | 801k | m_reachable[parent_chunk_idx].second = top_children - top_info.transactions; |
941 | 801k | m_reachable[child_chunk_idx].first = bottom_parents - bottom_info.transactions; |
942 | 801k | m_reachable[child_chunk_idx].second = bottom_children - bottom_info.transactions; |
943 | | // Return the two new set idxs. |
944 | 801k | m_cost.DeactivateEnd(/*num_deps=*/ntx - 1); |
945 | 801k | return {parent_chunk_idx, child_chunk_idx}; |
946 | 801k | } _ZN17cluster_linearize19SpanningForestStateIN13bitset_detail9IntBitSetIjEENS_19SFLDefaultCostModelEE10DeactivateEjj Line | Count | Source | 892 | 10.7k | { | 893 | 10.7k | m_cost.DeactivateBegin(); | 894 | | // Gather and check information about the parent transactions. | 895 | 10.7k | auto& parent_data = m_tx_data[parent_idx]; | 896 | 10.7k | Assume(parent_data.children[child_idx]); | 897 | 10.7k | Assume(parent_data.active_children[child_idx]); | 898 | | // Get the top set of the active dependency (which will become the parent chunk) and the | 899 | | // chunk set the transactions are currently in (which will become the bottom chunk). | 900 | 10.7k | auto parent_chunk_idx = parent_data.dep_top_idx[child_idx]; | 901 | 10.7k | auto child_chunk_idx = parent_data.chunk_idx; | 902 | 10.7k | Assume(parent_chunk_idx != child_chunk_idx); | 903 | 10.7k | Assume(m_chunk_idxs[child_chunk_idx]); | 904 | 10.7k | Assume(!m_chunk_idxs[parent_chunk_idx]); // top set, not a chunk | 905 | 10.7k | auto& top_info = m_set_info[parent_chunk_idx]; | 906 | 10.7k | auto& bottom_info = m_set_info[child_chunk_idx]; | 907 | | | 908 | | // Remove the active dependency. | 909 | 10.7k | parent_data.active_children.Reset(child_idx); | 910 | 10.7k | m_chunk_idxs.Set(parent_chunk_idx); | 911 | 10.7k | auto ntx = bottom_info.transactions.Count(); | 912 | | // Subtract the top_info from the bottom_info, as it will become the child chunk. | 913 | 10.7k | bottom_info -= top_info; | 914 | | // See the comment above in Activate(). We perform the opposite operations here, removing | 915 | | // instead of adding. Simultaneously, aggregate the top/bottom's union of parents/children. | 916 | 10.7k | SetType top_parents, top_children; | 917 | 69.8k | for (auto tx_idx : top_info.transactions) { Branch (917:26): [True: 69.8k, False: 10.7k]
| 918 | 69.8k | auto& tx_data = m_tx_data[tx_idx]; | 919 | 69.8k | tx_data.chunk_idx = parent_chunk_idx; | 920 | 69.8k | top_parents |= tx_data.parents; | 921 | 69.8k | top_children |= tx_data.children; | 922 | 69.8k | for (auto dep_child_idx : tx_data.active_children) { Branch (922:37): [True: 59.0k, False: 69.8k]
| 923 | 59.0k | auto& dep_top_info = m_set_info[tx_data.dep_top_idx[dep_child_idx]]; | 924 | 59.0k | if (dep_top_info.transactions[parent_idx]) dep_top_info -= bottom_info; Branch (924:21): [True: 25.0k, False: 33.9k]
| 925 | 59.0k | } | 926 | 69.8k | } | 927 | 10.7k | SetType bottom_parents, bottom_children; | 928 | 66.5k | for (auto tx_idx : bottom_info.transactions) { Branch (928:26): [True: 66.5k, False: 10.7k]
| 929 | 66.5k | auto& tx_data = m_tx_data[tx_idx]; | 930 | 66.5k | bottom_parents |= tx_data.parents; | 931 | 66.5k | bottom_children |= tx_data.children; | 932 | 66.5k | for (auto dep_child_idx : tx_data.active_children) { Branch (932:37): [True: 55.7k, False: 66.5k]
| 933 | 55.7k | auto& dep_top_info = m_set_info[tx_data.dep_top_idx[dep_child_idx]]; | 934 | 55.7k | if (dep_top_info.transactions[child_idx]) dep_top_info -= top_info; Branch (934:21): [True: 24.3k, False: 31.4k]
| 935 | 55.7k | } | 936 | 66.5k | } | 937 | | // Compute the new sets of reachable transactions for each new chunk, based on the | 938 | | // top/bottom parents and children computed above. | 939 | 10.7k | m_reachable[parent_chunk_idx].first = top_parents - top_info.transactions; | 940 | 10.7k | m_reachable[parent_chunk_idx].second = top_children - top_info.transactions; | 941 | 10.7k | m_reachable[child_chunk_idx].first = bottom_parents - bottom_info.transactions; | 942 | 10.7k | m_reachable[child_chunk_idx].second = bottom_children - bottom_info.transactions; | 943 | | // Return the two new set idxs. | 944 | 10.7k | m_cost.DeactivateEnd(/*num_deps=*/ntx - 1); | 945 | 10.7k | return {parent_chunk_idx, child_chunk_idx}; | 946 | 10.7k | } |
_ZN17cluster_linearize19SpanningForestStateIN13bitset_detail14MultiIntBitSetImLj2EEENS_19SFLDefaultCostModelEE10DeactivateEjj Line | Count | Source | 892 | 4.55k | { | 893 | 4.55k | m_cost.DeactivateBegin(); | 894 | | // Gather and check information about the parent transactions. | 895 | 4.55k | auto& parent_data = m_tx_data[parent_idx]; | 896 | 4.55k | Assume(parent_data.children[child_idx]); | 897 | 4.55k | Assume(parent_data.active_children[child_idx]); | 898 | | // Get the top set of the active dependency (which will become the parent chunk) and the | 899 | | // chunk set the transactions are currently in (which will become the bottom chunk). | 900 | 4.55k | auto parent_chunk_idx = parent_data.dep_top_idx[child_idx]; | 901 | 4.55k | auto child_chunk_idx = parent_data.chunk_idx; | 902 | 4.55k | Assume(parent_chunk_idx != child_chunk_idx); | 903 | 4.55k | Assume(m_chunk_idxs[child_chunk_idx]); | 904 | 4.55k | Assume(!m_chunk_idxs[parent_chunk_idx]); // top set, not a chunk | 905 | 4.55k | auto& top_info = m_set_info[parent_chunk_idx]; | 906 | 4.55k | auto& bottom_info = m_set_info[child_chunk_idx]; | 907 | | | 908 | | // Remove the active dependency. | 909 | 4.55k | parent_data.active_children.Reset(child_idx); | 910 | 4.55k | m_chunk_idxs.Set(parent_chunk_idx); | 911 | 4.55k | auto ntx = bottom_info.transactions.Count(); | 912 | | // Subtract the top_info from the bottom_info, as it will become the child chunk. | 913 | 4.55k | bottom_info -= top_info; | 914 | | // See the comment above in Activate(). We perform the opposite operations here, removing | 915 | | // instead of adding. Simultaneously, aggregate the top/bottom's union of parents/children. | 916 | 4.55k | SetType top_parents, top_children; | 917 | 14.9k | for (auto tx_idx : top_info.transactions) { Branch (917:26): [True: 14.9k, False: 4.55k]
| 918 | 14.9k | auto& tx_data = m_tx_data[tx_idx]; | 919 | 14.9k | tx_data.chunk_idx = parent_chunk_idx; | 920 | 14.9k | top_parents |= tx_data.parents; | 921 | 14.9k | top_children |= tx_data.children; | 922 | 14.9k | for (auto dep_child_idx : tx_data.active_children) { Branch (922:37): [True: 10.4k, False: 14.9k]
| 923 | 10.4k | auto& dep_top_info = m_set_info[tx_data.dep_top_idx[dep_child_idx]]; | 924 | 10.4k | if (dep_top_info.transactions[parent_idx]) dep_top_info -= bottom_info; Branch (924:21): [True: 5.04k, False: 5.39k]
| 925 | 10.4k | } | 926 | 14.9k | } | 927 | 4.55k | SetType bottom_parents, bottom_children; | 928 | 11.7k | for (auto tx_idx : bottom_info.transactions) { Branch (928:26): [True: 11.7k, False: 4.55k]
| 929 | 11.7k | auto& tx_data = m_tx_data[tx_idx]; | 930 | 11.7k | bottom_parents |= tx_data.parents; | 931 | 11.7k | bottom_children |= tx_data.children; | 932 | 11.7k | for (auto dep_child_idx : tx_data.active_children) { Branch (932:37): [True: 7.22k, False: 11.7k]
| 933 | 7.22k | auto& dep_top_info = m_set_info[tx_data.dep_top_idx[dep_child_idx]]; | 934 | 7.22k | if (dep_top_info.transactions[child_idx]) dep_top_info -= top_info; Branch (934:21): [True: 3.86k, False: 3.35k]
| 935 | 7.22k | } | 936 | 11.7k | } | 937 | | // Compute the new sets of reachable transactions for each new chunk, based on the | 938 | | // top/bottom parents and children computed above. | 939 | 4.55k | m_reachable[parent_chunk_idx].first = top_parents - top_info.transactions; | 940 | 4.55k | m_reachable[parent_chunk_idx].second = top_children - top_info.transactions; | 941 | 4.55k | m_reachable[child_chunk_idx].first = bottom_parents - bottom_info.transactions; | 942 | 4.55k | m_reachable[child_chunk_idx].second = bottom_children - bottom_info.transactions; | 943 | | // Return the two new set idxs. | 944 | 4.55k | m_cost.DeactivateEnd(/*num_deps=*/ntx - 1); | 945 | 4.55k | return {parent_chunk_idx, child_chunk_idx}; | 946 | 4.55k | } |
_ZN17cluster_linearize19SpanningForestStateIN13bitset_detail9IntBitSetImEENS_19SFLDefaultCostModelEE10DeactivateEjj Line | Count | Source | 892 | 786k | { | 893 | 786k | m_cost.DeactivateBegin(); | 894 | | // Gather and check information about the parent transactions. | 895 | 786k | auto& parent_data = m_tx_data[parent_idx]; | 896 | 786k | Assume(parent_data.children[child_idx]); | 897 | 786k | Assume(parent_data.active_children[child_idx]); | 898 | | // Get the top set of the active dependency (which will become the parent chunk) and the | 899 | | // chunk set the transactions are currently in (which will become the bottom chunk). | 900 | 786k | auto parent_chunk_idx = parent_data.dep_top_idx[child_idx]; | 901 | 786k | auto child_chunk_idx = parent_data.chunk_idx; | 902 | 786k | Assume(parent_chunk_idx != child_chunk_idx); | 903 | 786k | Assume(m_chunk_idxs[child_chunk_idx]); | 904 | 786k | Assume(!m_chunk_idxs[parent_chunk_idx]); // top set, not a chunk | 905 | 786k | auto& top_info = m_set_info[parent_chunk_idx]; | 906 | 786k | auto& bottom_info = m_set_info[child_chunk_idx]; | 907 | | | 908 | | // Remove the active dependency. | 909 | 786k | parent_data.active_children.Reset(child_idx); | 910 | 786k | m_chunk_idxs.Set(parent_chunk_idx); | 911 | 786k | auto ntx = bottom_info.transactions.Count(); | 912 | | // Subtract the top_info from the bottom_info, as it will become the child chunk. | 913 | 786k | bottom_info -= top_info; | 914 | | // See the comment above in Activate(). We perform the opposite operations here, removing | 915 | | // instead of adding. Simultaneously, aggregate the top/bottom's union of parents/children. | 916 | 786k | SetType top_parents, top_children; | 917 | 4.95M | for (auto tx_idx : top_info.transactions) { Branch (917:26): [True: 4.95M, False: 786k]
| 918 | 4.95M | auto& tx_data = m_tx_data[tx_idx]; | 919 | 4.95M | tx_data.chunk_idx = parent_chunk_idx; | 920 | 4.95M | top_parents |= tx_data.parents; | 921 | 4.95M | top_children |= tx_data.children; | 922 | 4.95M | for (auto dep_child_idx : tx_data.active_children) { Branch (922:37): [True: 4.16M, False: 4.95M]
| 923 | 4.16M | auto& dep_top_info = m_set_info[tx_data.dep_top_idx[dep_child_idx]]; | 924 | 4.16M | if (dep_top_info.transactions[parent_idx]) dep_top_info -= bottom_info; Branch (924:21): [True: 3.40M, False: 763k]
| 925 | 4.16M | } | 926 | 4.95M | } | 927 | 786k | SetType bottom_parents, bottom_children; | 928 | 1.62M | for (auto tx_idx : bottom_info.transactions) { Branch (928:26): [True: 1.62M, False: 786k]
| 929 | 1.62M | auto& tx_data = m_tx_data[tx_idx]; | 930 | 1.62M | bottom_parents |= tx_data.parents; | 931 | 1.62M | bottom_children |= tx_data.children; | 932 | 1.62M | for (auto dep_child_idx : tx_data.active_children) { Branch (932:37): [True: 834k, False: 1.62M]
| 933 | 834k | auto& dep_top_info = m_set_info[tx_data.dep_top_idx[dep_child_idx]]; | 934 | 834k | if (dep_top_info.transactions[child_idx]) dep_top_info -= top_info; Branch (934:21): [True: 545k, False: 288k]
| 935 | 834k | } | 936 | 1.62M | } | 937 | | // Compute the new sets of reachable transactions for each new chunk, based on the | 938 | | // top/bottom parents and children computed above. | 939 | 786k | m_reachable[parent_chunk_idx].first = top_parents - top_info.transactions; | 940 | 786k | m_reachable[parent_chunk_idx].second = top_children - top_info.transactions; | 941 | 786k | m_reachable[child_chunk_idx].first = bottom_parents - bottom_info.transactions; | 942 | 786k | m_reachable[child_chunk_idx].second = bottom_children - bottom_info.transactions; | 943 | | // Return the two new set idxs. | 944 | 786k | m_cost.DeactivateEnd(/*num_deps=*/ntx - 1); | 945 | 786k | return {parent_chunk_idx, child_chunk_idx}; | 946 | 786k | } |
|
947 | | |
948 | | /** Activate a dependency from the bottom set to the top set, which must exist. Return the |
949 | | * index of the merged chunk. */ |
950 | | SetIdx MergeChunks(SetIdx top_idx, SetIdx bottom_idx) noexcept |
951 | 2.96M | { |
952 | 2.96M | m_cost.MergeChunksBegin(); |
953 | 2.96M | Assume(m_chunk_idxs[top_idx]); |
954 | 2.96M | Assume(m_chunk_idxs[bottom_idx]); |
955 | 2.96M | auto& top_chunk_info = m_set_info[top_idx]; |
956 | 2.96M | auto& bottom_chunk_info = m_set_info[bottom_idx]; |
957 | | // Count the number of dependencies between bottom_chunk and top_chunk, remembering the |
958 | | // per-transaction counts so the picking loop below does not need to recompute the |
959 | | // intersections. |
960 | 2.96M | unsigned num_deps{0}; |
961 | 2.96M | std::array<SetIdx, SetType::Size()> counts; |
962 | 16.4M | for (auto tx_idx : top_chunk_info.transactions) { Branch (962:26): [True: 122k, False: 23.4k]
Branch (962:26): [True: 28.6k, False: 7.89k]
Branch (962:26): [True: 16.2M, False: 2.92M]
|
963 | 16.4M | auto& tx_data = m_tx_data[tx_idx]; |
964 | 16.4M | auto count = (tx_data.children & bottom_chunk_info.transactions).Count(); |
965 | 16.4M | counts[tx_idx] = count; |
966 | 16.4M | num_deps += count; |
967 | 16.4M | } |
968 | 2.96M | m_cost.MergeChunksMid(/*num_txns=*/top_chunk_info.transactions.Count()); |
969 | 2.96M | Assume(num_deps > 0); |
970 | | // Uniformly randomly pick one of them and activate it. |
971 | 2.96M | unsigned pick = m_rng.randrange(num_deps); |
972 | 2.96M | unsigned num_steps = 0; |
973 | 8.88M | for (auto tx_idx : top_chunk_info.transactions) { Branch (973:26): [True: 82.6k, False: 0]
Branch (973:26): [True: 17.1k, False: 0]
Branch (973:26): [True: 8.78M, False: 0]
|
974 | 8.88M | ++num_steps; |
975 | 8.88M | auto count = counts[tx_idx]; |
976 | 8.88M | if (pick < count) { Branch (976:17): [True: 23.4k, False: 59.1k]
Branch (976:17): [True: 7.89k, False: 9.22k]
Branch (976:17): [True: 2.92M, False: 5.85M]
|
977 | 2.96M | auto& tx_data = m_tx_data[tx_idx]; |
978 | 2.96M | auto intersect = tx_data.children & bottom_chunk_info.transactions; |
979 | 2.98M | for (auto child_idx : intersect) { Branch (979:37): [True: 24.5k, False: 0]
Branch (979:37): [True: 7.96k, False: 0]
Branch (979:37): [True: 2.95M, False: 0]
|
980 | 2.98M | if (pick == 0) { Branch (980:25): [True: 23.4k, False: 1.12k]
Branch (980:25): [True: 7.89k, False: 67]
Branch (980:25): [True: 2.92M, False: 28.4k]
|
981 | 2.96M | m_cost.MergeChunksEnd(/*num_steps=*/num_steps); |
982 | 2.96M | return Activate(tx_idx, child_idx); |
983 | 2.96M | } |
984 | 29.6k | --pick; |
985 | 29.6k | } |
986 | 0 | Assume(false); |
987 | 0 | break; |
988 | 2.96M | } |
989 | 5.92M | pick -= count; |
990 | 5.92M | } |
991 | 0 | Assume(false); |
992 | 0 | return INVALID_SET_IDX; |
993 | 2.96M | } _ZN17cluster_linearize19SpanningForestStateIN13bitset_detail9IntBitSetIjEENS_19SFLDefaultCostModelEE11MergeChunksEhh Line | Count | Source | 951 | 23.4k | { | 952 | 23.4k | m_cost.MergeChunksBegin(); | 953 | 23.4k | Assume(m_chunk_idxs[top_idx]); | 954 | 23.4k | Assume(m_chunk_idxs[bottom_idx]); | 955 | 23.4k | auto& top_chunk_info = m_set_info[top_idx]; | 956 | 23.4k | auto& bottom_chunk_info = m_set_info[bottom_idx]; | 957 | | // Count the number of dependencies between bottom_chunk and top_chunk, remembering the | 958 | | // per-transaction counts so the picking loop below does not need to recompute the | 959 | | // intersections. | 960 | 23.4k | unsigned num_deps{0}; | 961 | 23.4k | std::array<SetIdx, SetType::Size()> counts; | 962 | 122k | for (auto tx_idx : top_chunk_info.transactions) { Branch (962:26): [True: 122k, False: 23.4k]
| 963 | 122k | auto& tx_data = m_tx_data[tx_idx]; | 964 | 122k | auto count = (tx_data.children & bottom_chunk_info.transactions).Count(); | 965 | 122k | counts[tx_idx] = count; | 966 | 122k | num_deps += count; | 967 | 122k | } | 968 | 23.4k | m_cost.MergeChunksMid(/*num_txns=*/top_chunk_info.transactions.Count()); | 969 | 23.4k | Assume(num_deps > 0); | 970 | | // Uniformly randomly pick one of them and activate it. | 971 | 23.4k | unsigned pick = m_rng.randrange(num_deps); | 972 | 23.4k | unsigned num_steps = 0; | 973 | 82.6k | for (auto tx_idx : top_chunk_info.transactions) { Branch (973:26): [True: 82.6k, False: 0]
| 974 | 82.6k | ++num_steps; | 975 | 82.6k | auto count = counts[tx_idx]; | 976 | 82.6k | if (pick < count) { Branch (976:17): [True: 23.4k, False: 59.1k]
| 977 | 23.4k | auto& tx_data = m_tx_data[tx_idx]; | 978 | 23.4k | auto intersect = tx_data.children & bottom_chunk_info.transactions; | 979 | 24.5k | for (auto child_idx : intersect) { Branch (979:37): [True: 24.5k, False: 0]
| 980 | 24.5k | if (pick == 0) { Branch (980:25): [True: 23.4k, False: 1.12k]
| 981 | 23.4k | m_cost.MergeChunksEnd(/*num_steps=*/num_steps); | 982 | 23.4k | return Activate(tx_idx, child_idx); | 983 | 23.4k | } | 984 | 1.12k | --pick; | 985 | 1.12k | } | 986 | 0 | Assume(false); | 987 | 0 | break; | 988 | 23.4k | } | 989 | 59.1k | pick -= count; | 990 | 59.1k | } | 991 | 0 | Assume(false); | 992 | 0 | return INVALID_SET_IDX; | 993 | 23.4k | } |
_ZN17cluster_linearize19SpanningForestStateIN13bitset_detail14MultiIntBitSetImLj2EEENS_19SFLDefaultCostModelEE11MergeChunksEhh Line | Count | Source | 951 | 7.89k | { | 952 | 7.89k | m_cost.MergeChunksBegin(); | 953 | 7.89k | Assume(m_chunk_idxs[top_idx]); | 954 | 7.89k | Assume(m_chunk_idxs[bottom_idx]); | 955 | 7.89k | auto& top_chunk_info = m_set_info[top_idx]; | 956 | 7.89k | auto& bottom_chunk_info = m_set_info[bottom_idx]; | 957 | | // Count the number of dependencies between bottom_chunk and top_chunk, remembering the | 958 | | // per-transaction counts so the picking loop below does not need to recompute the | 959 | | // intersections. | 960 | 7.89k | unsigned num_deps{0}; | 961 | 7.89k | std::array<SetIdx, SetType::Size()> counts; | 962 | 28.6k | for (auto tx_idx : top_chunk_info.transactions) { Branch (962:26): [True: 28.6k, False: 7.89k]
| 963 | 28.6k | auto& tx_data = m_tx_data[tx_idx]; | 964 | 28.6k | auto count = (tx_data.children & bottom_chunk_info.transactions).Count(); | 965 | 28.6k | counts[tx_idx] = count; | 966 | 28.6k | num_deps += count; | 967 | 28.6k | } | 968 | 7.89k | m_cost.MergeChunksMid(/*num_txns=*/top_chunk_info.transactions.Count()); | 969 | 7.89k | Assume(num_deps > 0); | 970 | | // Uniformly randomly pick one of them and activate it. | 971 | 7.89k | unsigned pick = m_rng.randrange(num_deps); | 972 | 7.89k | unsigned num_steps = 0; | 973 | 17.1k | for (auto tx_idx : top_chunk_info.transactions) { Branch (973:26): [True: 17.1k, False: 0]
| 974 | 17.1k | ++num_steps; | 975 | 17.1k | auto count = counts[tx_idx]; | 976 | 17.1k | if (pick < count) { Branch (976:17): [True: 7.89k, False: 9.22k]
| 977 | 7.89k | auto& tx_data = m_tx_data[tx_idx]; | 978 | 7.89k | auto intersect = tx_data.children & bottom_chunk_info.transactions; | 979 | 7.96k | for (auto child_idx : intersect) { Branch (979:37): [True: 7.96k, False: 0]
| 980 | 7.96k | if (pick == 0) { Branch (980:25): [True: 7.89k, False: 67]
| 981 | 7.89k | m_cost.MergeChunksEnd(/*num_steps=*/num_steps); | 982 | 7.89k | return Activate(tx_idx, child_idx); | 983 | 7.89k | } | 984 | 67 | --pick; | 985 | 67 | } | 986 | 0 | Assume(false); | 987 | 0 | break; | 988 | 7.89k | } | 989 | 9.22k | pick -= count; | 990 | 9.22k | } | 991 | 0 | Assume(false); | 992 | 0 | return INVALID_SET_IDX; | 993 | 7.89k | } |
_ZN17cluster_linearize19SpanningForestStateIN13bitset_detail9IntBitSetImEENS_19SFLDefaultCostModelEE11MergeChunksEhh Line | Count | Source | 951 | 2.92M | { | 952 | 2.92M | m_cost.MergeChunksBegin(); | 953 | 2.92M | Assume(m_chunk_idxs[top_idx]); | 954 | 2.92M | Assume(m_chunk_idxs[bottom_idx]); | 955 | 2.92M | auto& top_chunk_info = m_set_info[top_idx]; | 956 | 2.92M | auto& bottom_chunk_info = m_set_info[bottom_idx]; | 957 | | // Count the number of dependencies between bottom_chunk and top_chunk, remembering the | 958 | | // per-transaction counts so the picking loop below does not need to recompute the | 959 | | // intersections. | 960 | 2.92M | unsigned num_deps{0}; | 961 | 2.92M | std::array<SetIdx, SetType::Size()> counts; | 962 | 16.2M | for (auto tx_idx : top_chunk_info.transactions) { Branch (962:26): [True: 16.2M, False: 2.92M]
| 963 | 16.2M | auto& tx_data = m_tx_data[tx_idx]; | 964 | 16.2M | auto count = (tx_data.children & bottom_chunk_info.transactions).Count(); | 965 | 16.2M | counts[tx_idx] = count; | 966 | 16.2M | num_deps += count; | 967 | 16.2M | } | 968 | 2.92M | m_cost.MergeChunksMid(/*num_txns=*/top_chunk_info.transactions.Count()); | 969 | 2.92M | Assume(num_deps > 0); | 970 | | // Uniformly randomly pick one of them and activate it. | 971 | 2.92M | unsigned pick = m_rng.randrange(num_deps); | 972 | 2.92M | unsigned num_steps = 0; | 973 | 8.78M | for (auto tx_idx : top_chunk_info.transactions) { Branch (973:26): [True: 8.78M, False: 0]
| 974 | 8.78M | ++num_steps; | 975 | 8.78M | auto count = counts[tx_idx]; | 976 | 8.78M | if (pick < count) { Branch (976:17): [True: 2.92M, False: 5.85M]
| 977 | 2.92M | auto& tx_data = m_tx_data[tx_idx]; | 978 | 2.92M | auto intersect = tx_data.children & bottom_chunk_info.transactions; | 979 | 2.95M | for (auto child_idx : intersect) { Branch (979:37): [True: 2.95M, False: 0]
| 980 | 2.95M | if (pick == 0) { Branch (980:25): [True: 2.92M, False: 28.4k]
| 981 | 2.92M | m_cost.MergeChunksEnd(/*num_steps=*/num_steps); | 982 | 2.92M | return Activate(tx_idx, child_idx); | 983 | 2.92M | } | 984 | 28.4k | --pick; | 985 | 28.4k | } | 986 | 0 | Assume(false); | 987 | 0 | break; | 988 | 2.92M | } | 989 | 5.85M | pick -= count; | 990 | 5.85M | } | 991 | 0 | Assume(false); | 992 | 0 | return INVALID_SET_IDX; | 993 | 2.92M | } |
|
994 | | |
995 | | /** Activate a dependency from chunk_idx to merge_chunk_idx (if !DownWard), or a dependency |
996 | | * from merge_chunk_idx to chunk_idx (if DownWard). Return the index of the merged chunk. */ |
997 | | template<bool DownWard> |
998 | | SetIdx MergeChunksDirected(SetIdx chunk_idx, SetIdx merge_chunk_idx) noexcept |
999 | 2.88M | { |
1000 | 2.88M | if constexpr (DownWard) { |
1001 | 14.5k | return MergeChunks(chunk_idx, merge_chunk_idx); |
1002 | 2.87M | } else { |
1003 | 2.87M | return MergeChunks(merge_chunk_idx, chunk_idx); |
1004 | 2.87M | } |
1005 | 2.88M | } _ZN17cluster_linearize19SpanningForestStateIN13bitset_detail9IntBitSetIjEENS_19SFLDefaultCostModelEE19MergeChunksDirectedILb0EEEhhh Line | Count | Source | 999 | 15.9k | { | 1000 | | if constexpr (DownWard) { | 1001 | | return MergeChunks(chunk_idx, merge_chunk_idx); | 1002 | 15.9k | } else { | 1003 | 15.9k | return MergeChunks(merge_chunk_idx, chunk_idx); | 1004 | 15.9k | } | 1005 | 15.9k | } |
_ZN17cluster_linearize19SpanningForestStateIN13bitset_detail9IntBitSetIjEENS_19SFLDefaultCostModelEE19MergeChunksDirectedILb1EEEhhh Line | Count | Source | 999 | 4.45k | { | 1000 | 4.45k | if constexpr (DownWard) { | 1001 | 4.45k | return MergeChunks(chunk_idx, merge_chunk_idx); | 1002 | | } else { | 1003 | | return MergeChunks(merge_chunk_idx, chunk_idx); | 1004 | | } | 1005 | 4.45k | } |
_ZN17cluster_linearize19SpanningForestStateIN13bitset_detail14MultiIntBitSetImLj2EEENS_19SFLDefaultCostModelEE19MergeChunksDirectedILb0EEEhhh Line | Count | Source | 999 | 7.37k | { | 1000 | | if constexpr (DownWard) { | 1001 | | return MergeChunks(chunk_idx, merge_chunk_idx); | 1002 | 7.37k | } else { | 1003 | 7.37k | return MergeChunks(merge_chunk_idx, chunk_idx); | 1004 | 7.37k | } | 1005 | 7.37k | } |
Unexecuted instantiation: _ZN17cluster_linearize19SpanningForestStateIN13bitset_detail14MultiIntBitSetImLj2EEENS_19SFLDefaultCostModelEE19MergeChunksDirectedILb1EEEhhh _ZN17cluster_linearize19SpanningForestStateIN13bitset_detail9IntBitSetImEENS_19SFLDefaultCostModelEE19MergeChunksDirectedILb0EEEhhh Line | Count | Source | 999 | 2.85M | { | 1000 | | if constexpr (DownWard) { | 1001 | | return MergeChunks(chunk_idx, merge_chunk_idx); | 1002 | 2.85M | } else { | 1003 | 2.85M | return MergeChunks(merge_chunk_idx, chunk_idx); | 1004 | 2.85M | } | 1005 | 2.85M | } |
_ZN17cluster_linearize19SpanningForestStateIN13bitset_detail9IntBitSetImEENS_19SFLDefaultCostModelEE19MergeChunksDirectedILb1EEEhhh Line | Count | Source | 999 | 10.0k | { | 1000 | 10.0k | if constexpr (DownWard) { | 1001 | 10.0k | return MergeChunks(chunk_idx, merge_chunk_idx); | 1002 | | } else { | 1003 | | return MergeChunks(merge_chunk_idx, chunk_idx); | 1004 | | } | 1005 | 10.0k | } |
|
1006 | | |
1007 | | /** Determine which chunk to merge chunk_idx with, or INVALID_SET_IDX if none. */ |
1008 | | template<bool DownWard> |
1009 | | SetIdx PickMergeCandidate(SetIdx chunk_idx) noexcept |
1010 | 12.2M | { |
1011 | 12.2M | m_cost.PickMergeCandidateBegin(); |
1012 | | /** Information about the chunk. */ |
1013 | 12.2M | Assume(m_chunk_idxs[chunk_idx]); |
1014 | 12.2M | auto& chunk_info = m_set_info[chunk_idx]; |
1015 | | // Iterate over all chunks reachable from this one. For those depended-on chunks, |
1016 | | // remember the highest-feerate (if DownWard) or lowest-feerate (if !DownWard) one. |
1017 | | // If multiple equal-feerate candidate chunks to merge with exist, pick a random one |
1018 | | // among them. |
1019 | | |
1020 | | /** The minimum feerate (if downward) or maximum feerate (if upward) to consider when |
1021 | | * looking for candidate chunks to merge with. Initially, this is the original chunk's |
1022 | | * feerate, but is updated to be the current best candidate whenever one is found. */ |
1023 | 12.2M | FeeFrac best_other_chunk_feerate = chunk_info.feerate; |
1024 | | /** The chunk index for the best candidate chunk to merge with. INVALID_SET_IDX if none. */ |
1025 | 12.2M | SetIdx best_other_chunk_idx = INVALID_SET_IDX; |
1026 | | /** We generate random tiebreak values to pick between equal-feerate candidate chunks. |
1027 | | * This variable stores the tiebreak of the current best candidate. */ |
1028 | 12.2M | uint64_t best_other_chunk_tiebreak{0}; |
1029 | | |
1030 | | /** Which parent/child transactions we still need to process the chunks for. */ |
1031 | 12.2M | auto todo = DownWard ? m_reachable[chunk_idx].second : m_reachable[chunk_idx].first; Branch (1031:21): [Folded - Ignored]
Branch (1031:21): [Folded - Ignored]
Branch (1031:21): [Folded - Ignored]
Branch (1031:21): [Folded - Ignored]
Branch (1031:21): [Folded - Ignored]
Branch (1031:21): [Folded - Ignored]
|
1032 | 12.2M | unsigned steps = 0; |
1033 | 22.9M | while (todo.Any()) { Branch (1033:16): [True: 64.8k, False: 38.0k]
Branch (1033:16): [True: 14.7k, False: 12.1k]
Branch (1033:16): [True: 15.8k, False: 28.5k]
Branch (1033:16): [True: 0, False: 0]
Branch (1033:16): [True: 9.67M, False: 10.8M]
Branch (1033:16): [True: 921k, False: 1.33M]
|
1034 | 10.6M | ++steps; |
1035 | | // Find a chunk for a transaction in todo, and remove all its transactions from todo. |
1036 | 10.6M | auto reached_chunk_idx = m_tx_data[todo.First()].chunk_idx; |
1037 | 10.6M | auto& reached_chunk_info = m_set_info[reached_chunk_idx]; |
1038 | 10.6M | todo -= reached_chunk_info.transactions; |
1039 | | // See if it has an acceptable feerate. |
1040 | 10.6M | auto cmp = DownWard ? ByRatio{best_other_chunk_feerate} <=> ByRatio{reached_chunk_info.feerate} Branch (1040:24): [Folded - Ignored]
Branch (1040:24): [Folded - Ignored]
Branch (1040:24): [Folded - Ignored]
Branch (1040:24): [Folded - Ignored]
Branch (1040:24): [Folded - Ignored]
Branch (1040:24): [Folded - Ignored]
|
1041 | 10.6M | : ByRatio{reached_chunk_info.feerate} <=> ByRatio{best_other_chunk_feerate}; |
1042 | 10.6M | if (cmp > 0) continue; Branch (1042:17): [True: 27.6k, False: 37.2k]
Branch (1042:17): [True: 7.35k, False: 7.40k]
Branch (1042:17): [True: 6.05k, False: 9.82k]
Branch (1042:17): [True: 0, False: 0]
Branch (1042:17): [True: 6.35M, False: 3.32M]
Branch (1042:17): [True: 905k, False: 15.8k]
|
1043 | 3.39M | uint64_t tiebreak = m_rng.rand64(); |
1044 | 3.39M | if (cmp < 0 || tiebreak >= best_other_chunk_tiebreak) { Branch (1044:17): [True: 13.1k, False: 24.0k]
Branch (1044:28): [True: 10.3k, False: 13.7k]
Branch (1044:17): [True: 3.51k, False: 3.89k]
Branch (1044:28): [True: 2.63k, False: 1.25k]
Branch (1044:17): [True: 2.85k, False: 6.96k]
Branch (1044:28): [True: 5.74k, False: 1.21k]
Branch (1044:17): [True: 0, False: 0]
Branch (1044:28): [True: 0, False: 0]
Branch (1044:17): [True: 2.24M, False: 1.08M]
Branch (1044:28): [True: 912k, False: 169k]
Branch (1044:17): [True: 7.57k, False: 8.29k]
Branch (1044:28): [True: 4.74k, False: 3.55k]
|
1045 | 3.20M | best_other_chunk_feerate = reached_chunk_info.feerate; |
1046 | 3.20M | best_other_chunk_idx = reached_chunk_idx; |
1047 | 3.20M | best_other_chunk_tiebreak = tiebreak; |
1048 | 3.20M | } |
1049 | 3.39M | } |
1050 | 12.2M | Assume(steps <= m_set_info.size()); |
1051 | | |
1052 | 12.2M | m_cost.PickMergeCandidateEnd(/*num_steps=*/steps); |
1053 | 12.2M | return best_other_chunk_idx; |
1054 | 12.2M | } _ZN17cluster_linearize19SpanningForestStateIN13bitset_detail9IntBitSetIjEENS_19SFLDefaultCostModelEE18PickMergeCandidateILb0EEEhh Line | Count | Source | 1010 | 38.0k | { | 1011 | 38.0k | m_cost.PickMergeCandidateBegin(); | 1012 | | /** Information about the chunk. */ | 1013 | 38.0k | Assume(m_chunk_idxs[chunk_idx]); | 1014 | 38.0k | auto& chunk_info = m_set_info[chunk_idx]; | 1015 | | // Iterate over all chunks reachable from this one. For those depended-on chunks, | 1016 | | // remember the highest-feerate (if DownWard) or lowest-feerate (if !DownWard) one. | 1017 | | // If multiple equal-feerate candidate chunks to merge with exist, pick a random one | 1018 | | // among them. | 1019 | | | 1020 | | /** The minimum feerate (if downward) or maximum feerate (if upward) to consider when | 1021 | | * looking for candidate chunks to merge with. Initially, this is the original chunk's | 1022 | | * feerate, but is updated to be the current best candidate whenever one is found. */ | 1023 | 38.0k | FeeFrac best_other_chunk_feerate = chunk_info.feerate; | 1024 | | /** The chunk index for the best candidate chunk to merge with. INVALID_SET_IDX if none. */ | 1025 | 38.0k | SetIdx best_other_chunk_idx = INVALID_SET_IDX; | 1026 | | /** We generate random tiebreak values to pick between equal-feerate candidate chunks. | 1027 | | * This variable stores the tiebreak of the current best candidate. */ | 1028 | 38.0k | uint64_t best_other_chunk_tiebreak{0}; | 1029 | | | 1030 | | /** Which parent/child transactions we still need to process the chunks for. */ | 1031 | 38.0k | auto todo = DownWard ? m_reachable[chunk_idx].second : m_reachable[chunk_idx].first; Branch (1031:21): [Folded - Ignored]
| 1032 | 38.0k | unsigned steps = 0; | 1033 | 102k | while (todo.Any()) { Branch (1033:16): [True: 64.8k, False: 38.0k]
| 1034 | 64.8k | ++steps; | 1035 | | // Find a chunk for a transaction in todo, and remove all its transactions from todo. | 1036 | 64.8k | auto reached_chunk_idx = m_tx_data[todo.First()].chunk_idx; | 1037 | 64.8k | auto& reached_chunk_info = m_set_info[reached_chunk_idx]; | 1038 | 64.8k | todo -= reached_chunk_info.transactions; | 1039 | | // See if it has an acceptable feerate. | 1040 | 64.8k | auto cmp = DownWard ? ByRatio{best_other_chunk_feerate} <=> ByRatio{reached_chunk_info.feerate} Branch (1040:24): [Folded - Ignored]
| 1041 | 64.8k | : ByRatio{reached_chunk_info.feerate} <=> ByRatio{best_other_chunk_feerate}; | 1042 | 64.8k | if (cmp > 0) continue; Branch (1042:17): [True: 27.6k, False: 37.2k]
| 1043 | 37.2k | uint64_t tiebreak = m_rng.rand64(); | 1044 | 37.2k | if (cmp < 0 || tiebreak >= best_other_chunk_tiebreak) { Branch (1044:17): [True: 13.1k, False: 24.0k]
Branch (1044:28): [True: 10.3k, False: 13.7k]
| 1045 | 23.5k | best_other_chunk_feerate = reached_chunk_info.feerate; | 1046 | 23.5k | best_other_chunk_idx = reached_chunk_idx; | 1047 | 23.5k | best_other_chunk_tiebreak = tiebreak; | 1048 | 23.5k | } | 1049 | 37.2k | } | 1050 | 38.0k | Assume(steps <= m_set_info.size()); | 1051 | | | 1052 | 38.0k | m_cost.PickMergeCandidateEnd(/*num_steps=*/steps); | 1053 | 38.0k | return best_other_chunk_idx; | 1054 | 38.0k | } |
_ZN17cluster_linearize19SpanningForestStateIN13bitset_detail9IntBitSetIjEENS_19SFLDefaultCostModelEE18PickMergeCandidateILb1EEEhh Line | Count | Source | 1010 | 12.1k | { | 1011 | 12.1k | m_cost.PickMergeCandidateBegin(); | 1012 | | /** Information about the chunk. */ | 1013 | 12.1k | Assume(m_chunk_idxs[chunk_idx]); | 1014 | 12.1k | auto& chunk_info = m_set_info[chunk_idx]; | 1015 | | // Iterate over all chunks reachable from this one. For those depended-on chunks, | 1016 | | // remember the highest-feerate (if DownWard) or lowest-feerate (if !DownWard) one. | 1017 | | // If multiple equal-feerate candidate chunks to merge with exist, pick a random one | 1018 | | // among them. | 1019 | | | 1020 | | /** The minimum feerate (if downward) or maximum feerate (if upward) to consider when | 1021 | | * looking for candidate chunks to merge with. Initially, this is the original chunk's | 1022 | | * feerate, but is updated to be the current best candidate whenever one is found. */ | 1023 | 12.1k | FeeFrac best_other_chunk_feerate = chunk_info.feerate; | 1024 | | /** The chunk index for the best candidate chunk to merge with. INVALID_SET_IDX if none. */ | 1025 | 12.1k | SetIdx best_other_chunk_idx = INVALID_SET_IDX; | 1026 | | /** We generate random tiebreak values to pick between equal-feerate candidate chunks. | 1027 | | * This variable stores the tiebreak of the current best candidate. */ | 1028 | 12.1k | uint64_t best_other_chunk_tiebreak{0}; | 1029 | | | 1030 | | /** Which parent/child transactions we still need to process the chunks for. */ | 1031 | 12.1k | auto todo = DownWard ? m_reachable[chunk_idx].second : m_reachable[chunk_idx].first; Branch (1031:21): [Folded - Ignored]
| 1032 | 12.1k | unsigned steps = 0; | 1033 | 26.8k | while (todo.Any()) { Branch (1033:16): [True: 14.7k, False: 12.1k]
| 1034 | 14.7k | ++steps; | 1035 | | // Find a chunk for a transaction in todo, and remove all its transactions from todo. | 1036 | 14.7k | auto reached_chunk_idx = m_tx_data[todo.First()].chunk_idx; | 1037 | 14.7k | auto& reached_chunk_info = m_set_info[reached_chunk_idx]; | 1038 | 14.7k | todo -= reached_chunk_info.transactions; | 1039 | | // See if it has an acceptable feerate. | 1040 | 14.7k | auto cmp = DownWard ? ByRatio{best_other_chunk_feerate} <=> ByRatio{reached_chunk_info.feerate} Branch (1040:24): [Folded - Ignored]
| 1041 | 14.7k | : ByRatio{reached_chunk_info.feerate} <=> ByRatio{best_other_chunk_feerate}; | 1042 | 14.7k | if (cmp > 0) continue; Branch (1042:17): [True: 7.35k, False: 7.40k]
| 1043 | 7.40k | uint64_t tiebreak = m_rng.rand64(); | 1044 | 7.40k | if (cmp < 0 || tiebreak >= best_other_chunk_tiebreak) { Branch (1044:17): [True: 3.51k, False: 3.89k]
Branch (1044:28): [True: 2.63k, False: 1.25k]
| 1045 | 6.14k | best_other_chunk_feerate = reached_chunk_info.feerate; | 1046 | 6.14k | best_other_chunk_idx = reached_chunk_idx; | 1047 | 6.14k | best_other_chunk_tiebreak = tiebreak; | 1048 | 6.14k | } | 1049 | 7.40k | } | 1050 | 12.1k | Assume(steps <= m_set_info.size()); | 1051 | | | 1052 | 12.1k | m_cost.PickMergeCandidateEnd(/*num_steps=*/steps); | 1053 | 12.1k | return best_other_chunk_idx; | 1054 | 12.1k | } |
_ZN17cluster_linearize19SpanningForestStateIN13bitset_detail14MultiIntBitSetImLj2EEENS_19SFLDefaultCostModelEE18PickMergeCandidateILb0EEEhh Line | Count | Source | 1010 | 28.5k | { | 1011 | 28.5k | m_cost.PickMergeCandidateBegin(); | 1012 | | /** Information about the chunk. */ | 1013 | 28.5k | Assume(m_chunk_idxs[chunk_idx]); | 1014 | 28.5k | auto& chunk_info = m_set_info[chunk_idx]; | 1015 | | // Iterate over all chunks reachable from this one. For those depended-on chunks, | 1016 | | // remember the highest-feerate (if DownWard) or lowest-feerate (if !DownWard) one. | 1017 | | // If multiple equal-feerate candidate chunks to merge with exist, pick a random one | 1018 | | // among them. | 1019 | | | 1020 | | /** The minimum feerate (if downward) or maximum feerate (if upward) to consider when | 1021 | | * looking for candidate chunks to merge with. Initially, this is the original chunk's | 1022 | | * feerate, but is updated to be the current best candidate whenever one is found. */ | 1023 | 28.5k | FeeFrac best_other_chunk_feerate = chunk_info.feerate; | 1024 | | /** The chunk index for the best candidate chunk to merge with. INVALID_SET_IDX if none. */ | 1025 | 28.5k | SetIdx best_other_chunk_idx = INVALID_SET_IDX; | 1026 | | /** We generate random tiebreak values to pick between equal-feerate candidate chunks. | 1027 | | * This variable stores the tiebreak of the current best candidate. */ | 1028 | 28.5k | uint64_t best_other_chunk_tiebreak{0}; | 1029 | | | 1030 | | /** Which parent/child transactions we still need to process the chunks for. */ | 1031 | 28.5k | auto todo = DownWard ? m_reachable[chunk_idx].second : m_reachable[chunk_idx].first; Branch (1031:21): [Folded - Ignored]
| 1032 | 28.5k | unsigned steps = 0; | 1033 | 44.4k | while (todo.Any()) { Branch (1033:16): [True: 15.8k, False: 28.5k]
| 1034 | 15.8k | ++steps; | 1035 | | // Find a chunk for a transaction in todo, and remove all its transactions from todo. | 1036 | 15.8k | auto reached_chunk_idx = m_tx_data[todo.First()].chunk_idx; | 1037 | 15.8k | auto& reached_chunk_info = m_set_info[reached_chunk_idx]; | 1038 | 15.8k | todo -= reached_chunk_info.transactions; | 1039 | | // See if it has an acceptable feerate. | 1040 | 15.8k | auto cmp = DownWard ? ByRatio{best_other_chunk_feerate} <=> ByRatio{reached_chunk_info.feerate} Branch (1040:24): [Folded - Ignored]
| 1041 | 15.8k | : ByRatio{reached_chunk_info.feerate} <=> ByRatio{best_other_chunk_feerate}; | 1042 | 15.8k | if (cmp > 0) continue; Branch (1042:17): [True: 6.05k, False: 9.82k]
| 1043 | 9.82k | uint64_t tiebreak = m_rng.rand64(); | 1044 | 9.82k | if (cmp < 0 || tiebreak >= best_other_chunk_tiebreak) { Branch (1044:17): [True: 2.85k, False: 6.96k]
Branch (1044:28): [True: 5.74k, False: 1.21k]
| 1045 | 8.60k | best_other_chunk_feerate = reached_chunk_info.feerate; | 1046 | 8.60k | best_other_chunk_idx = reached_chunk_idx; | 1047 | 8.60k | best_other_chunk_tiebreak = tiebreak; | 1048 | 8.60k | } | 1049 | 9.82k | } | 1050 | 28.5k | Assume(steps <= m_set_info.size()); | 1051 | | | 1052 | 28.5k | m_cost.PickMergeCandidateEnd(/*num_steps=*/steps); | 1053 | 28.5k | return best_other_chunk_idx; | 1054 | 28.5k | } |
Unexecuted instantiation: _ZN17cluster_linearize19SpanningForestStateIN13bitset_detail14MultiIntBitSetImLj2EEENS_19SFLDefaultCostModelEE18PickMergeCandidateILb1EEEhh _ZN17cluster_linearize19SpanningForestStateIN13bitset_detail9IntBitSetImEENS_19SFLDefaultCostModelEE18PickMergeCandidateILb0EEEhh Line | Count | Source | 1010 | 10.8M | { | 1011 | 10.8M | m_cost.PickMergeCandidateBegin(); | 1012 | | /** Information about the chunk. */ | 1013 | 10.8M | Assume(m_chunk_idxs[chunk_idx]); | 1014 | 10.8M | auto& chunk_info = m_set_info[chunk_idx]; | 1015 | | // Iterate over all chunks reachable from this one. For those depended-on chunks, | 1016 | | // remember the highest-feerate (if DownWard) or lowest-feerate (if !DownWard) one. | 1017 | | // If multiple equal-feerate candidate chunks to merge with exist, pick a random one | 1018 | | // among them. | 1019 | | | 1020 | | /** The minimum feerate (if downward) or maximum feerate (if upward) to consider when | 1021 | | * looking for candidate chunks to merge with. Initially, this is the original chunk's | 1022 | | * feerate, but is updated to be the current best candidate whenever one is found. */ | 1023 | 10.8M | FeeFrac best_other_chunk_feerate = chunk_info.feerate; | 1024 | | /** The chunk index for the best candidate chunk to merge with. INVALID_SET_IDX if none. */ | 1025 | 10.8M | SetIdx best_other_chunk_idx = INVALID_SET_IDX; | 1026 | | /** We generate random tiebreak values to pick between equal-feerate candidate chunks. | 1027 | | * This variable stores the tiebreak of the current best candidate. */ | 1028 | 10.8M | uint64_t best_other_chunk_tiebreak{0}; | 1029 | | | 1030 | | /** Which parent/child transactions we still need to process the chunks for. */ | 1031 | 10.8M | auto todo = DownWard ? m_reachable[chunk_idx].second : m_reachable[chunk_idx].first; Branch (1031:21): [Folded - Ignored]
| 1032 | 10.8M | unsigned steps = 0; | 1033 | 20.5M | while (todo.Any()) { Branch (1033:16): [True: 9.67M, False: 10.8M]
| 1034 | 9.67M | ++steps; | 1035 | | // Find a chunk for a transaction in todo, and remove all its transactions from todo. | 1036 | 9.67M | auto reached_chunk_idx = m_tx_data[todo.First()].chunk_idx; | 1037 | 9.67M | auto& reached_chunk_info = m_set_info[reached_chunk_idx]; | 1038 | 9.67M | todo -= reached_chunk_info.transactions; | 1039 | | // See if it has an acceptable feerate. | 1040 | 9.67M | auto cmp = DownWard ? ByRatio{best_other_chunk_feerate} <=> ByRatio{reached_chunk_info.feerate} Branch (1040:24): [Folded - Ignored]
| 1041 | 9.67M | : ByRatio{reached_chunk_info.feerate} <=> ByRatio{best_other_chunk_feerate}; | 1042 | 9.67M | if (cmp > 0) continue; Branch (1042:17): [True: 6.35M, False: 3.32M]
| 1043 | 3.32M | uint64_t tiebreak = m_rng.rand64(); | 1044 | 3.32M | if (cmp < 0 || tiebreak >= best_other_chunk_tiebreak) { Branch (1044:17): [True: 2.24M, False: 1.08M]
Branch (1044:28): [True: 912k, False: 169k]
| 1045 | 3.15M | best_other_chunk_feerate = reached_chunk_info.feerate; | 1046 | 3.15M | best_other_chunk_idx = reached_chunk_idx; | 1047 | 3.15M | best_other_chunk_tiebreak = tiebreak; | 1048 | 3.15M | } | 1049 | 3.32M | } | 1050 | 10.8M | Assume(steps <= m_set_info.size()); | 1051 | | | 1052 | 10.8M | m_cost.PickMergeCandidateEnd(/*num_steps=*/steps); | 1053 | 10.8M | return best_other_chunk_idx; | 1054 | 10.8M | } |
_ZN17cluster_linearize19SpanningForestStateIN13bitset_detail9IntBitSetImEENS_19SFLDefaultCostModelEE18PickMergeCandidateILb1EEEhh Line | Count | Source | 1010 | 1.33M | { | 1011 | 1.33M | m_cost.PickMergeCandidateBegin(); | 1012 | | /** Information about the chunk. */ | 1013 | 1.33M | Assume(m_chunk_idxs[chunk_idx]); | 1014 | 1.33M | auto& chunk_info = m_set_info[chunk_idx]; | 1015 | | // Iterate over all chunks reachable from this one. For those depended-on chunks, | 1016 | | // remember the highest-feerate (if DownWard) or lowest-feerate (if !DownWard) one. | 1017 | | // If multiple equal-feerate candidate chunks to merge with exist, pick a random one | 1018 | | // among them. | 1019 | | | 1020 | | /** The minimum feerate (if downward) or maximum feerate (if upward) to consider when | 1021 | | * looking for candidate chunks to merge with. Initially, this is the original chunk's | 1022 | | * feerate, but is updated to be the current best candidate whenever one is found. */ | 1023 | 1.33M | FeeFrac best_other_chunk_feerate = chunk_info.feerate; | 1024 | | /** The chunk index for the best candidate chunk to merge with. INVALID_SET_IDX if none. */ | 1025 | 1.33M | SetIdx best_other_chunk_idx = INVALID_SET_IDX; | 1026 | | /** We generate random tiebreak values to pick between equal-feerate candidate chunks. | 1027 | | * This variable stores the tiebreak of the current best candidate. */ | 1028 | 1.33M | uint64_t best_other_chunk_tiebreak{0}; | 1029 | | | 1030 | | /** Which parent/child transactions we still need to process the chunks for. */ | 1031 | 1.33M | auto todo = DownWard ? m_reachable[chunk_idx].second : m_reachable[chunk_idx].first; Branch (1031:21): [Folded - Ignored]
| 1032 | 1.33M | unsigned steps = 0; | 1033 | 2.26M | while (todo.Any()) { Branch (1033:16): [True: 921k, False: 1.33M]
| 1034 | 921k | ++steps; | 1035 | | // Find a chunk for a transaction in todo, and remove all its transactions from todo. | 1036 | 921k | auto reached_chunk_idx = m_tx_data[todo.First()].chunk_idx; | 1037 | 921k | auto& reached_chunk_info = m_set_info[reached_chunk_idx]; | 1038 | 921k | todo -= reached_chunk_info.transactions; | 1039 | | // See if it has an acceptable feerate. | 1040 | 921k | auto cmp = DownWard ? ByRatio{best_other_chunk_feerate} <=> ByRatio{reached_chunk_info.feerate} Branch (1040:24): [Folded - Ignored]
| 1041 | 921k | : ByRatio{reached_chunk_info.feerate} <=> ByRatio{best_other_chunk_feerate}; | 1042 | 921k | if (cmp > 0) continue; Branch (1042:17): [True: 905k, False: 15.8k]
| 1043 | 15.8k | uint64_t tiebreak = m_rng.rand64(); | 1044 | 15.8k | if (cmp < 0 || tiebreak >= best_other_chunk_tiebreak) { Branch (1044:17): [True: 7.57k, False: 8.29k]
Branch (1044:28): [True: 4.74k, False: 3.55k]
| 1045 | 12.3k | best_other_chunk_feerate = reached_chunk_info.feerate; | 1046 | 12.3k | best_other_chunk_idx = reached_chunk_idx; | 1047 | 12.3k | best_other_chunk_tiebreak = tiebreak; | 1048 | 12.3k | } | 1049 | 15.8k | } | 1050 | 1.33M | Assume(steps <= m_set_info.size()); | 1051 | | | 1052 | 1.33M | m_cost.PickMergeCandidateEnd(/*num_steps=*/steps); | 1053 | 1.33M | return best_other_chunk_idx; | 1054 | 1.33M | } |
|
1055 | | |
1056 | | /** Perform an upward or downward merge step, on the specified chunk. Returns the merged chunk, |
1057 | | * or INVALID_SET_IDX if no merge took place. */ |
1058 | | template<bool DownWard> |
1059 | | SetIdx MergeStep(SetIdx chunk_idx) noexcept |
1060 | 12.2M | { |
1061 | 12.2M | auto merge_chunk_idx = PickMergeCandidate<DownWard>(chunk_idx); |
1062 | 12.2M | if (merge_chunk_idx == INVALID_SET_IDX) return INVALID_SET_IDX; Branch (1062:13): [True: 22.1k, False: 15.9k]
Branch (1062:13): [True: 7.68k, False: 4.45k]
Branch (1062:13): [True: 21.1k, False: 7.37k]
Branch (1062:13): [True: 0, False: 0]
Branch (1062:13): [True: 8.02M, False: 2.85M]
Branch (1062:13): [True: 1.32M, False: 10.0k]
|
1063 | 2.88M | chunk_idx = MergeChunksDirected<DownWard>(chunk_idx, merge_chunk_idx); |
1064 | 2.88M | Assume(chunk_idx != INVALID_SET_IDX); |
1065 | 2.88M | return chunk_idx; |
1066 | 12.2M | } _ZN17cluster_linearize19SpanningForestStateIN13bitset_detail9IntBitSetIjEENS_19SFLDefaultCostModelEE9MergeStepILb0EEEhh Line | Count | Source | 1060 | 38.0k | { | 1061 | 38.0k | auto merge_chunk_idx = PickMergeCandidate<DownWard>(chunk_idx); | 1062 | 38.0k | if (merge_chunk_idx == INVALID_SET_IDX) return INVALID_SET_IDX; Branch (1062:13): [True: 22.1k, False: 15.9k]
| 1063 | 15.9k | chunk_idx = MergeChunksDirected<DownWard>(chunk_idx, merge_chunk_idx); | 1064 | 15.9k | Assume(chunk_idx != INVALID_SET_IDX); | 1065 | 15.9k | return chunk_idx; | 1066 | 38.0k | } |
_ZN17cluster_linearize19SpanningForestStateIN13bitset_detail9IntBitSetIjEENS_19SFLDefaultCostModelEE9MergeStepILb1EEEhh Line | Count | Source | 1060 | 12.1k | { | 1061 | 12.1k | auto merge_chunk_idx = PickMergeCandidate<DownWard>(chunk_idx); | 1062 | 12.1k | if (merge_chunk_idx == INVALID_SET_IDX) return INVALID_SET_IDX; Branch (1062:13): [True: 7.68k, False: 4.45k]
| 1063 | 4.45k | chunk_idx = MergeChunksDirected<DownWard>(chunk_idx, merge_chunk_idx); | 1064 | 4.45k | Assume(chunk_idx != INVALID_SET_IDX); | 1065 | 4.45k | return chunk_idx; | 1066 | 12.1k | } |
_ZN17cluster_linearize19SpanningForestStateIN13bitset_detail14MultiIntBitSetImLj2EEENS_19SFLDefaultCostModelEE9MergeStepILb0EEEhh Line | Count | Source | 1060 | 28.5k | { | 1061 | 28.5k | auto merge_chunk_idx = PickMergeCandidate<DownWard>(chunk_idx); | 1062 | 28.5k | if (merge_chunk_idx == INVALID_SET_IDX) return INVALID_SET_IDX; Branch (1062:13): [True: 21.1k, False: 7.37k]
| 1063 | 7.37k | chunk_idx = MergeChunksDirected<DownWard>(chunk_idx, merge_chunk_idx); | 1064 | 7.37k | Assume(chunk_idx != INVALID_SET_IDX); | 1065 | 7.37k | return chunk_idx; | 1066 | 28.5k | } |
Unexecuted instantiation: _ZN17cluster_linearize19SpanningForestStateIN13bitset_detail14MultiIntBitSetImLj2EEENS_19SFLDefaultCostModelEE9MergeStepILb1EEEhh _ZN17cluster_linearize19SpanningForestStateIN13bitset_detail9IntBitSetImEENS_19SFLDefaultCostModelEE9MergeStepILb0EEEhh Line | Count | Source | 1060 | 10.8M | { | 1061 | 10.8M | auto merge_chunk_idx = PickMergeCandidate<DownWard>(chunk_idx); | 1062 | 10.8M | if (merge_chunk_idx == INVALID_SET_IDX) return INVALID_SET_IDX; Branch (1062:13): [True: 8.02M, False: 2.85M]
| 1063 | 2.85M | chunk_idx = MergeChunksDirected<DownWard>(chunk_idx, merge_chunk_idx); | 1064 | 2.85M | Assume(chunk_idx != INVALID_SET_IDX); | 1065 | 2.85M | return chunk_idx; | 1066 | 10.8M | } |
_ZN17cluster_linearize19SpanningForestStateIN13bitset_detail9IntBitSetImEENS_19SFLDefaultCostModelEE9MergeStepILb1EEEhh Line | Count | Source | 1060 | 1.33M | { | 1061 | 1.33M | auto merge_chunk_idx = PickMergeCandidate<DownWard>(chunk_idx); | 1062 | 1.33M | if (merge_chunk_idx == INVALID_SET_IDX) return INVALID_SET_IDX; Branch (1062:13): [True: 1.32M, False: 10.0k]
| 1063 | 10.0k | chunk_idx = MergeChunksDirected<DownWard>(chunk_idx, merge_chunk_idx); | 1064 | 10.0k | Assume(chunk_idx != INVALID_SET_IDX); | 1065 | 10.0k | return chunk_idx; | 1066 | 1.33M | } |
|
1067 | | |
1068 | | /** Perform an upward or downward merge sequence on the specified chunk. */ |
1069 | | template<bool DownWard> |
1070 | | void MergeSequence(SetIdx chunk_idx) noexcept |
1071 | 172k | { |
1072 | 172k | Assume(m_chunk_idxs[chunk_idx]); |
1073 | 183k | while (true) { Branch (1073:16): [Folded - Ignored]
Branch (1073:16): [Folded - Ignored]
Branch (1073:16): [Folded - Ignored]
Branch (1073:16): [Folded - Ignored]
Branch (1073:16): [Folded - Ignored]
Branch (1073:16): [Folded - Ignored]
|
1074 | 183k | auto merged_chunk_idx = MergeStep<DownWard>(chunk_idx); |
1075 | 183k | if (merged_chunk_idx == INVALID_SET_IDX) break; Branch (1075:17): [True: 2.43k, False: 824]
Branch (1075:17): [True: 2.43k, False: 853]
Branch (1075:17): [True: 0, False: 0]
Branch (1075:17): [True: 0, False: 0]
Branch (1075:17): [True: 83.9k, False: 3.03k]
Branch (1075:17): [True: 83.9k, False: 6.13k]
|
1076 | 10.8k | chunk_idx = merged_chunk_idx; |
1077 | 10.8k | } |
1078 | | // Add the chunk to the queue of improvable chunks, if it wasn't already there. |
1079 | 172k | if (!m_suboptimal_idxs[chunk_idx]) { Branch (1079:13): [True: 2.42k, False: 16]
Branch (1079:13): [True: 2.16k, False: 269]
Branch (1079:13): [True: 0, False: 0]
Branch (1079:13): [True: 0, False: 0]
Branch (1079:13): [True: 83.8k, False: 9]
Branch (1079:13): [True: 81.3k, False: 2.59k]
|
1080 | 169k | m_suboptimal_idxs.Set(chunk_idx); |
1081 | 169k | m_suboptimal_chunks.push_back(chunk_idx); |
1082 | 169k | } |
1083 | 172k | } _ZN17cluster_linearize19SpanningForestStateIN13bitset_detail9IntBitSetIjEENS_19SFLDefaultCostModelEE13MergeSequenceILb0EEEvh Line | Count | Source | 1071 | 2.43k | { | 1072 | 2.43k | Assume(m_chunk_idxs[chunk_idx]); | 1073 | 3.26k | while (true) { Branch (1073:16): [Folded - Ignored]
| 1074 | 3.26k | auto merged_chunk_idx = MergeStep<DownWard>(chunk_idx); | 1075 | 3.26k | if (merged_chunk_idx == INVALID_SET_IDX) break; Branch (1075:17): [True: 2.43k, False: 824]
| 1076 | 824 | chunk_idx = merged_chunk_idx; | 1077 | 824 | } | 1078 | | // Add the chunk to the queue of improvable chunks, if it wasn't already there. | 1079 | 2.43k | if (!m_suboptimal_idxs[chunk_idx]) { Branch (1079:13): [True: 2.42k, False: 16]
| 1080 | 2.42k | m_suboptimal_idxs.Set(chunk_idx); | 1081 | 2.42k | m_suboptimal_chunks.push_back(chunk_idx); | 1082 | 2.42k | } | 1083 | 2.43k | } |
_ZN17cluster_linearize19SpanningForestStateIN13bitset_detail9IntBitSetIjEENS_19SFLDefaultCostModelEE13MergeSequenceILb1EEEvh Line | Count | Source | 1071 | 2.43k | { | 1072 | 2.43k | Assume(m_chunk_idxs[chunk_idx]); | 1073 | 3.29k | while (true) { Branch (1073:16): [Folded - Ignored]
| 1074 | 3.29k | auto merged_chunk_idx = MergeStep<DownWard>(chunk_idx); | 1075 | 3.29k | if (merged_chunk_idx == INVALID_SET_IDX) break; Branch (1075:17): [True: 2.43k, False: 853]
| 1076 | 853 | chunk_idx = merged_chunk_idx; | 1077 | 853 | } | 1078 | | // Add the chunk to the queue of improvable chunks, if it wasn't already there. | 1079 | 2.43k | if (!m_suboptimal_idxs[chunk_idx]) { Branch (1079:13): [True: 2.16k, False: 269]
| 1080 | 2.16k | m_suboptimal_idxs.Set(chunk_idx); | 1081 | 2.16k | m_suboptimal_chunks.push_back(chunk_idx); | 1082 | 2.16k | } | 1083 | 2.43k | } |
Unexecuted instantiation: _ZN17cluster_linearize19SpanningForestStateIN13bitset_detail14MultiIntBitSetImLj2EEENS_19SFLDefaultCostModelEE13MergeSequenceILb0EEEvh Unexecuted instantiation: _ZN17cluster_linearize19SpanningForestStateIN13bitset_detail14MultiIntBitSetImLj2EEENS_19SFLDefaultCostModelEE13MergeSequenceILb1EEEvh _ZN17cluster_linearize19SpanningForestStateIN13bitset_detail9IntBitSetImEENS_19SFLDefaultCostModelEE13MergeSequenceILb0EEEvh Line | Count | Source | 1071 | 83.9k | { | 1072 | 83.9k | Assume(m_chunk_idxs[chunk_idx]); | 1073 | 86.9k | while (true) { Branch (1073:16): [Folded - Ignored]
| 1074 | 86.9k | auto merged_chunk_idx = MergeStep<DownWard>(chunk_idx); | 1075 | 86.9k | if (merged_chunk_idx == INVALID_SET_IDX) break; Branch (1075:17): [True: 83.9k, False: 3.03k]
| 1076 | 3.03k | chunk_idx = merged_chunk_idx; | 1077 | 3.03k | } | 1078 | | // Add the chunk to the queue of improvable chunks, if it wasn't already there. | 1079 | 83.9k | if (!m_suboptimal_idxs[chunk_idx]) { Branch (1079:13): [True: 83.8k, False: 9]
| 1080 | 83.8k | m_suboptimal_idxs.Set(chunk_idx); | 1081 | 83.8k | m_suboptimal_chunks.push_back(chunk_idx); | 1082 | 83.8k | } | 1083 | 83.9k | } |
_ZN17cluster_linearize19SpanningForestStateIN13bitset_detail9IntBitSetImEENS_19SFLDefaultCostModelEE13MergeSequenceILb1EEEvh Line | Count | Source | 1071 | 83.9k | { | 1072 | 83.9k | Assume(m_chunk_idxs[chunk_idx]); | 1073 | 90.0k | while (true) { Branch (1073:16): [Folded - Ignored]
| 1074 | 90.0k | auto merged_chunk_idx = MergeStep<DownWard>(chunk_idx); | 1075 | 90.0k | if (merged_chunk_idx == INVALID_SET_IDX) break; Branch (1075:17): [True: 83.9k, False: 6.13k]
| 1076 | 6.13k | chunk_idx = merged_chunk_idx; | 1077 | 6.13k | } | 1078 | | // Add the chunk to the queue of improvable chunks, if it wasn't already there. | 1079 | 83.9k | if (!m_suboptimal_idxs[chunk_idx]) { Branch (1079:13): [True: 81.3k, False: 2.59k]
| 1080 | 81.3k | m_suboptimal_idxs.Set(chunk_idx); | 1081 | 81.3k | m_suboptimal_chunks.push_back(chunk_idx); | 1082 | 81.3k | } | 1083 | 83.9k | } |
|
1084 | | |
1085 | | /** Split a chunk, and then merge the resulting two chunks to make the graph topological |
1086 | | * again. */ |
1087 | | void Improve(TxIdx parent_idx, TxIdx child_idx) noexcept |
1088 | 128k | { |
1089 | | // Deactivate the specified dependency, splitting it into two new chunks: a top containing |
1090 | | // the parent, and a bottom containing the child. The top should have a higher feerate. |
1091 | 128k | auto [parent_chunk_idx, child_chunk_idx] = Deactivate(parent_idx, child_idx); |
1092 | | |
1093 | | // At this point we have exactly two chunks which may violate topology constraints (the |
1094 | | // parent chunk and child chunk that were produced by deactivation). We can fix |
1095 | | // these using just merge sequences, one upwards and one downwards, avoiding the need for a |
1096 | | // full MakeTopological. |
1097 | 128k | const auto& parent_reachable = m_reachable[parent_chunk_idx].first; |
1098 | 128k | const auto& child_chunk_txn = m_set_info[child_chunk_idx].transactions; |
1099 | 128k | if (parent_reachable.Overlaps(child_chunk_txn)) { Branch (1099:13): [True: 1.96k, False: 2.43k]
Branch (1099:13): [True: 204, False: 0]
Branch (1099:13): [True: 40.0k, False: 83.9k]
|
1100 | | // The parent chunk has a dependency on a transaction in the child chunk. In this case, |
1101 | | // the parent needs to merge back with the child chunk (a self-merge), and no other |
1102 | | // merges are needed. Special-case this, so the overhead of PickMergeCandidate and |
1103 | | // MergeSequence can be avoided. |
1104 | | |
1105 | | // In the self-merge, the roles reverse: the parent chunk (from the split) depends |
1106 | | // on the child chunk, so child_chunk_idx is the "top" and parent_chunk_idx is the |
1107 | | // "bottom" for MergeChunks. |
1108 | 42.2k | auto merged_chunk_idx = MergeChunks(child_chunk_idx, parent_chunk_idx); |
1109 | 42.2k | if (!m_suboptimal_idxs[merged_chunk_idx]) { Branch (1109:17): [True: 1.95k, False: 7]
Branch (1109:17): [True: 204, False: 0]
Branch (1109:17): [True: 40.0k, False: 1]
|
1110 | 42.2k | m_suboptimal_idxs.Set(merged_chunk_idx); |
1111 | 42.2k | m_suboptimal_chunks.push_back(merged_chunk_idx); |
1112 | 42.2k | } |
1113 | 86.3k | } else { |
1114 | | // Merge the top chunk with lower-feerate chunks it depends on. |
1115 | 86.3k | MergeSequence<false>(parent_chunk_idx); |
1116 | | // Merge the bottom chunk with higher-feerate chunks that depend on it. |
1117 | 86.3k | MergeSequence<true>(child_chunk_idx); |
1118 | 86.3k | } |
1119 | 128k | } _ZN17cluster_linearize19SpanningForestStateIN13bitset_detail9IntBitSetIjEENS_19SFLDefaultCostModelEE7ImproveEjj Line | Count | Source | 1088 | 4.40k | { | 1089 | | // Deactivate the specified dependency, splitting it into two new chunks: a top containing | 1090 | | // the parent, and a bottom containing the child. The top should have a higher feerate. | 1091 | 4.40k | auto [parent_chunk_idx, child_chunk_idx] = Deactivate(parent_idx, child_idx); | 1092 | | | 1093 | | // At this point we have exactly two chunks which may violate topology constraints (the | 1094 | | // parent chunk and child chunk that were produced by deactivation). We can fix | 1095 | | // these using just merge sequences, one upwards and one downwards, avoiding the need for a | 1096 | | // full MakeTopological. | 1097 | 4.40k | const auto& parent_reachable = m_reachable[parent_chunk_idx].first; | 1098 | 4.40k | const auto& child_chunk_txn = m_set_info[child_chunk_idx].transactions; | 1099 | 4.40k | if (parent_reachable.Overlaps(child_chunk_txn)) { Branch (1099:13): [True: 1.96k, False: 2.43k]
| 1100 | | // The parent chunk has a dependency on a transaction in the child chunk. In this case, | 1101 | | // the parent needs to merge back with the child chunk (a self-merge), and no other | 1102 | | // merges are needed. Special-case this, so the overhead of PickMergeCandidate and | 1103 | | // MergeSequence can be avoided. | 1104 | | | 1105 | | // In the self-merge, the roles reverse: the parent chunk (from the split) depends | 1106 | | // on the child chunk, so child_chunk_idx is the "top" and parent_chunk_idx is the | 1107 | | // "bottom" for MergeChunks. | 1108 | 1.96k | auto merged_chunk_idx = MergeChunks(child_chunk_idx, parent_chunk_idx); | 1109 | 1.96k | if (!m_suboptimal_idxs[merged_chunk_idx]) { Branch (1109:17): [True: 1.95k, False: 7]
| 1110 | 1.95k | m_suboptimal_idxs.Set(merged_chunk_idx); | 1111 | 1.95k | m_suboptimal_chunks.push_back(merged_chunk_idx); | 1112 | 1.95k | } | 1113 | 2.43k | } else { | 1114 | | // Merge the top chunk with lower-feerate chunks it depends on. | 1115 | 2.43k | MergeSequence<false>(parent_chunk_idx); | 1116 | | // Merge the bottom chunk with higher-feerate chunks that depend on it. | 1117 | 2.43k | MergeSequence<true>(child_chunk_idx); | 1118 | 2.43k | } | 1119 | 4.40k | } |
_ZN17cluster_linearize19SpanningForestStateIN13bitset_detail14MultiIntBitSetImLj2EEENS_19SFLDefaultCostModelEE7ImproveEjj Line | Count | Source | 1088 | 204 | { | 1089 | | // Deactivate the specified dependency, splitting it into two new chunks: a top containing | 1090 | | // the parent, and a bottom containing the child. The top should have a higher feerate. | 1091 | 204 | auto [parent_chunk_idx, child_chunk_idx] = Deactivate(parent_idx, child_idx); | 1092 | | | 1093 | | // At this point we have exactly two chunks which may violate topology constraints (the | 1094 | | // parent chunk and child chunk that were produced by deactivation). We can fix | 1095 | | // these using just merge sequences, one upwards and one downwards, avoiding the need for a | 1096 | | // full MakeTopological. | 1097 | 204 | const auto& parent_reachable = m_reachable[parent_chunk_idx].first; | 1098 | 204 | const auto& child_chunk_txn = m_set_info[child_chunk_idx].transactions; | 1099 | 204 | if (parent_reachable.Overlaps(child_chunk_txn)) { Branch (1099:13): [True: 204, False: 0]
| 1100 | | // The parent chunk has a dependency on a transaction in the child chunk. In this case, | 1101 | | // the parent needs to merge back with the child chunk (a self-merge), and no other | 1102 | | // merges are needed. Special-case this, so the overhead of PickMergeCandidate and | 1103 | | // MergeSequence can be avoided. | 1104 | | | 1105 | | // In the self-merge, the roles reverse: the parent chunk (from the split) depends | 1106 | | // on the child chunk, so child_chunk_idx is the "top" and parent_chunk_idx is the | 1107 | | // "bottom" for MergeChunks. | 1108 | 204 | auto merged_chunk_idx = MergeChunks(child_chunk_idx, parent_chunk_idx); | 1109 | 204 | if (!m_suboptimal_idxs[merged_chunk_idx]) { Branch (1109:17): [True: 204, False: 0]
| 1110 | 204 | m_suboptimal_idxs.Set(merged_chunk_idx); | 1111 | 204 | m_suboptimal_chunks.push_back(merged_chunk_idx); | 1112 | 204 | } | 1113 | 204 | } else { | 1114 | | // Merge the top chunk with lower-feerate chunks it depends on. | 1115 | 0 | MergeSequence<false>(parent_chunk_idx); | 1116 | | // Merge the bottom chunk with higher-feerate chunks that depend on it. | 1117 | 0 | MergeSequence<true>(child_chunk_idx); | 1118 | 0 | } | 1119 | 204 | } |
_ZN17cluster_linearize19SpanningForestStateIN13bitset_detail9IntBitSetImEENS_19SFLDefaultCostModelEE7ImproveEjj Line | Count | Source | 1088 | 123k | { | 1089 | | // Deactivate the specified dependency, splitting it into two new chunks: a top containing | 1090 | | // the parent, and a bottom containing the child. The top should have a higher feerate. | 1091 | 123k | auto [parent_chunk_idx, child_chunk_idx] = Deactivate(parent_idx, child_idx); | 1092 | | | 1093 | | // At this point we have exactly two chunks which may violate topology constraints (the | 1094 | | // parent chunk and child chunk that were produced by deactivation). We can fix | 1095 | | // these using just merge sequences, one upwards and one downwards, avoiding the need for a | 1096 | | // full MakeTopological. | 1097 | 123k | const auto& parent_reachable = m_reachable[parent_chunk_idx].first; | 1098 | 123k | const auto& child_chunk_txn = m_set_info[child_chunk_idx].transactions; | 1099 | 123k | if (parent_reachable.Overlaps(child_chunk_txn)) { Branch (1099:13): [True: 40.0k, False: 83.9k]
| 1100 | | // The parent chunk has a dependency on a transaction in the child chunk. In this case, | 1101 | | // the parent needs to merge back with the child chunk (a self-merge), and no other | 1102 | | // merges are needed. Special-case this, so the overhead of PickMergeCandidate and | 1103 | | // MergeSequence can be avoided. | 1104 | | | 1105 | | // In the self-merge, the roles reverse: the parent chunk (from the split) depends | 1106 | | // on the child chunk, so child_chunk_idx is the "top" and parent_chunk_idx is the | 1107 | | // "bottom" for MergeChunks. | 1108 | 40.0k | auto merged_chunk_idx = MergeChunks(child_chunk_idx, parent_chunk_idx); | 1109 | 40.0k | if (!m_suboptimal_idxs[merged_chunk_idx]) { Branch (1109:17): [True: 40.0k, False: 1]
| 1110 | 40.0k | m_suboptimal_idxs.Set(merged_chunk_idx); | 1111 | 40.0k | m_suboptimal_chunks.push_back(merged_chunk_idx); | 1112 | 40.0k | } | 1113 | 83.9k | } else { | 1114 | | // Merge the top chunk with lower-feerate chunks it depends on. | 1115 | 83.9k | MergeSequence<false>(parent_chunk_idx); | 1116 | | // Merge the bottom chunk with higher-feerate chunks that depend on it. | 1117 | 83.9k | MergeSequence<true>(child_chunk_idx); | 1118 | 83.9k | } | 1119 | 123k | } |
|
1120 | | |
1121 | | /** Determine the next chunk to optimize, or INVALID_SET_IDX if none. */ |
1122 | | SetIdx PickChunkToOptimize() noexcept |
1123 | 3.94M | { |
1124 | 3.94M | m_cost.PickChunkToOptimizeBegin(); |
1125 | 3.94M | unsigned steps{0}; |
1126 | 3.94M | while (!m_suboptimal_chunks.empty()) { Branch (1126:16): [True: 14.9k, False: 7]
Branch (1126:16): [True: 13.9k, False: 27]
Branch (1126:16): [True: 3.91M, False: 0]
|
1127 | 3.94M | ++steps; |
1128 | | // Pop an entry from the potentially-suboptimal chunk queue. |
1129 | 3.94M | SetIdx chunk_idx = m_suboptimal_chunks.front(); |
1130 | 3.94M | Assume(m_suboptimal_idxs[chunk_idx]); |
1131 | 3.94M | m_suboptimal_idxs.Reset(chunk_idx); |
1132 | 3.94M | m_suboptimal_chunks.pop_front(); |
1133 | 3.94M | if (m_chunk_idxs[chunk_idx]) { Branch (1133:17): [True: 14.6k, False: 306]
Branch (1133:17): [True: 13.9k, False: 0]
Branch (1133:17): [True: 3.91M, False: 1.58k]
|
1134 | 3.94M | m_cost.PickChunkToOptimizeEnd(/*num_steps=*/steps); |
1135 | 3.94M | return chunk_idx; |
1136 | 3.94M | } |
1137 | | // If what was popped is not currently a chunk, continue. This may |
1138 | | // happen when a split chunk merges in Improve() with one or more existing chunks that |
1139 | | // are themselves on the suboptimal queue already. |
1140 | 3.94M | } |
1141 | 34 | m_cost.PickChunkToOptimizeEnd(/*num_steps=*/steps); |
1142 | 34 | return INVALID_SET_IDX; |
1143 | 3.94M | } _ZN17cluster_linearize19SpanningForestStateIN13bitset_detail9IntBitSetIjEENS_19SFLDefaultCostModelEE19PickChunkToOptimizeEv Line | Count | Source | 1123 | 14.6k | { | 1124 | 14.6k | m_cost.PickChunkToOptimizeBegin(); | 1125 | 14.6k | unsigned steps{0}; | 1126 | 14.9k | while (!m_suboptimal_chunks.empty()) { Branch (1126:16): [True: 14.9k, False: 7]
| 1127 | 14.9k | ++steps; | 1128 | | // Pop an entry from the potentially-suboptimal chunk queue. | 1129 | 14.9k | SetIdx chunk_idx = m_suboptimal_chunks.front(); | 1130 | 14.9k | Assume(m_suboptimal_idxs[chunk_idx]); | 1131 | 14.9k | m_suboptimal_idxs.Reset(chunk_idx); | 1132 | 14.9k | m_suboptimal_chunks.pop_front(); | 1133 | 14.9k | if (m_chunk_idxs[chunk_idx]) { Branch (1133:17): [True: 14.6k, False: 306]
| 1134 | 14.6k | m_cost.PickChunkToOptimizeEnd(/*num_steps=*/steps); | 1135 | 14.6k | return chunk_idx; | 1136 | 14.6k | } | 1137 | | // If what was popped is not currently a chunk, continue. This may | 1138 | | // happen when a split chunk merges in Improve() with one or more existing chunks that | 1139 | | // are themselves on the suboptimal queue already. | 1140 | 14.9k | } | 1141 | 7 | m_cost.PickChunkToOptimizeEnd(/*num_steps=*/steps); | 1142 | 7 | return INVALID_SET_IDX; | 1143 | 14.6k | } |
_ZN17cluster_linearize19SpanningForestStateIN13bitset_detail14MultiIntBitSetImLj2EEENS_19SFLDefaultCostModelEE19PickChunkToOptimizeEv Line | Count | Source | 1123 | 14.0k | { | 1124 | 14.0k | m_cost.PickChunkToOptimizeBegin(); | 1125 | 14.0k | unsigned steps{0}; | 1126 | 14.0k | while (!m_suboptimal_chunks.empty()) { Branch (1126:16): [True: 13.9k, False: 27]
| 1127 | 13.9k | ++steps; | 1128 | | // Pop an entry from the potentially-suboptimal chunk queue. | 1129 | 13.9k | SetIdx chunk_idx = m_suboptimal_chunks.front(); | 1130 | 13.9k | Assume(m_suboptimal_idxs[chunk_idx]); | 1131 | 13.9k | m_suboptimal_idxs.Reset(chunk_idx); | 1132 | 13.9k | m_suboptimal_chunks.pop_front(); | 1133 | 13.9k | if (m_chunk_idxs[chunk_idx]) { Branch (1133:17): [True: 13.9k, False: 0]
| 1134 | 13.9k | m_cost.PickChunkToOptimizeEnd(/*num_steps=*/steps); | 1135 | 13.9k | return chunk_idx; | 1136 | 13.9k | } | 1137 | | // If what was popped is not currently a chunk, continue. This may | 1138 | | // happen when a split chunk merges in Improve() with one or more existing chunks that | 1139 | | // are themselves on the suboptimal queue already. | 1140 | 13.9k | } | 1141 | 27 | m_cost.PickChunkToOptimizeEnd(/*num_steps=*/steps); | 1142 | 27 | return INVALID_SET_IDX; | 1143 | 14.0k | } |
_ZN17cluster_linearize19SpanningForestStateIN13bitset_detail9IntBitSetImEENS_19SFLDefaultCostModelEE19PickChunkToOptimizeEv Line | Count | Source | 1123 | 3.91M | { | 1124 | 3.91M | m_cost.PickChunkToOptimizeBegin(); | 1125 | 3.91M | unsigned steps{0}; | 1126 | 3.91M | while (!m_suboptimal_chunks.empty()) { Branch (1126:16): [True: 3.91M, False: 0]
| 1127 | 3.91M | ++steps; | 1128 | | // Pop an entry from the potentially-suboptimal chunk queue. | 1129 | 3.91M | SetIdx chunk_idx = m_suboptimal_chunks.front(); | 1130 | 3.91M | Assume(m_suboptimal_idxs[chunk_idx]); | 1131 | 3.91M | m_suboptimal_idxs.Reset(chunk_idx); | 1132 | 3.91M | m_suboptimal_chunks.pop_front(); | 1133 | 3.91M | if (m_chunk_idxs[chunk_idx]) { Branch (1133:17): [True: 3.91M, False: 1.58k]
| 1134 | 3.91M | m_cost.PickChunkToOptimizeEnd(/*num_steps=*/steps); | 1135 | 3.91M | return chunk_idx; | 1136 | 3.91M | } | 1137 | | // If what was popped is not currently a chunk, continue. This may | 1138 | | // happen when a split chunk merges in Improve() with one or more existing chunks that | 1139 | | // are themselves on the suboptimal queue already. | 1140 | 3.91M | } | 1141 | 0 | m_cost.PickChunkToOptimizeEnd(/*num_steps=*/steps); | 1142 | 0 | return INVALID_SET_IDX; | 1143 | 3.91M | } |
|
1144 | | |
1145 | | /** Find a (parent, child) dependency to deactivate in chunk_idx, or (-1, -1) if none. */ |
1146 | | std::pair<TxIdx, TxIdx> PickDependencyToSplit(SetIdx chunk_idx) noexcept |
1147 | 3.94M | { |
1148 | 3.94M | m_cost.PickDependencyToSplitBegin(); |
1149 | 3.94M | Assume(m_chunk_idxs[chunk_idx]); |
1150 | 3.94M | auto& chunk_info = m_set_info[chunk_idx]; |
1151 | | |
1152 | | // Remember the best dependency {par, chl} seen so far. |
1153 | 3.94M | std::pair<TxIdx, TxIdx> candidate_dep = {TxIdx(-1), TxIdx(-1)}; |
1154 | 3.94M | uint64_t candidate_tiebreak = 0; |
1155 | | // Iterate over all transactions. |
1156 | 9.59M | for (auto tx_idx : chunk_info.transactions) { Branch (1156:26): [True: 106k, False: 14.6k]
Branch (1156:26): [True: 24.9k, False: 13.9k]
Branch (1156:26): [True: 9.46M, False: 3.91M]
|
1157 | 9.59M | const auto& tx_data = m_tx_data[tx_idx]; |
1158 | | // Iterate over all active child dependencies of the transaction. |
1159 | 9.59M | for (auto child_idx : tx_data.active_children) { Branch (1159:33): [True: 92.2k, False: 106k]
Branch (1159:33): [True: 10.9k, False: 24.9k]
Branch (1159:33): [True: 5.54M, False: 9.46M]
|
1160 | 5.65M | auto& dep_top_info = m_set_info[tx_data.dep_top_idx[child_idx]]; |
1161 | | // Skip if this dependency is ineligible (the top chunk that would be created |
1162 | | // does not have higher feerate than the chunk it is currently part of). |
1163 | 5.65M | auto cmp = ByRatio{dep_top_info.feerate} <=> ByRatio{chunk_info.feerate}; |
1164 | 5.65M | if (cmp <= 0) continue; Branch (1164:21): [True: 70.3k, False: 21.8k]
Branch (1164:21): [True: 10.3k, False: 601]
Branch (1164:21): [True: 4.10M, False: 1.44M]
|
1165 | | // Generate a random tiebreak for this dependency, and reject it if its tiebreak |
1166 | | // is worse than the best so far. This means that among all eligible |
1167 | | // dependencies, a uniformly random one will be chosen. |
1168 | 1.46M | uint64_t tiebreak = m_rng.rand64(); |
1169 | 1.46M | if (tiebreak < candidate_tiebreak) continue; Branch (1169:21): [True: 12.9k, False: 8.96k]
Branch (1169:21): [True: 251, False: 350]
Branch (1169:21): [True: 1.13M, False: 302k]
|
1170 | | // Remember this as our (new) candidate dependency. |
1171 | 311k | candidate_dep = {tx_idx, child_idx}; |
1172 | 311k | candidate_tiebreak = tiebreak; |
1173 | 311k | } |
1174 | 9.59M | } |
1175 | 3.94M | m_cost.PickDependencyToSplitEnd(/*num_txns=*/chunk_info.transactions.Count()); |
1176 | 3.94M | return candidate_dep; |
1177 | 3.94M | } _ZN17cluster_linearize19SpanningForestStateIN13bitset_detail9IntBitSetIjEENS_19SFLDefaultCostModelEE21PickDependencyToSplitEh Line | Count | Source | 1147 | 14.6k | { | 1148 | 14.6k | m_cost.PickDependencyToSplitBegin(); | 1149 | 14.6k | Assume(m_chunk_idxs[chunk_idx]); | 1150 | 14.6k | auto& chunk_info = m_set_info[chunk_idx]; | 1151 | | | 1152 | | // Remember the best dependency {par, chl} seen so far. | 1153 | 14.6k | std::pair<TxIdx, TxIdx> candidate_dep = {TxIdx(-1), TxIdx(-1)}; | 1154 | 14.6k | uint64_t candidate_tiebreak = 0; | 1155 | | // Iterate over all transactions. | 1156 | 106k | for (auto tx_idx : chunk_info.transactions) { Branch (1156:26): [True: 106k, False: 14.6k]
| 1157 | 106k | const auto& tx_data = m_tx_data[tx_idx]; | 1158 | | // Iterate over all active child dependencies of the transaction. | 1159 | 106k | for (auto child_idx : tx_data.active_children) { Branch (1159:33): [True: 92.2k, False: 106k]
| 1160 | 92.2k | auto& dep_top_info = m_set_info[tx_data.dep_top_idx[child_idx]]; | 1161 | | // Skip if this dependency is ineligible (the top chunk that would be created | 1162 | | // does not have higher feerate than the chunk it is currently part of). | 1163 | 92.2k | auto cmp = ByRatio{dep_top_info.feerate} <=> ByRatio{chunk_info.feerate}; | 1164 | 92.2k | if (cmp <= 0) continue; Branch (1164:21): [True: 70.3k, False: 21.8k]
| 1165 | | // Generate a random tiebreak for this dependency, and reject it if its tiebreak | 1166 | | // is worse than the best so far. This means that among all eligible | 1167 | | // dependencies, a uniformly random one will be chosen. | 1168 | 21.8k | uint64_t tiebreak = m_rng.rand64(); | 1169 | 21.8k | if (tiebreak < candidate_tiebreak) continue; Branch (1169:21): [True: 12.9k, False: 8.96k]
| 1170 | | // Remember this as our (new) candidate dependency. | 1171 | 8.96k | candidate_dep = {tx_idx, child_idx}; | 1172 | 8.96k | candidate_tiebreak = tiebreak; | 1173 | 8.96k | } | 1174 | 106k | } | 1175 | 14.6k | m_cost.PickDependencyToSplitEnd(/*num_txns=*/chunk_info.transactions.Count()); | 1176 | 14.6k | return candidate_dep; | 1177 | 14.6k | } |
_ZN17cluster_linearize19SpanningForestStateIN13bitset_detail14MultiIntBitSetImLj2EEENS_19SFLDefaultCostModelEE21PickDependencyToSplitEh Line | Count | Source | 1147 | 13.9k | { | 1148 | 13.9k | m_cost.PickDependencyToSplitBegin(); | 1149 | 13.9k | Assume(m_chunk_idxs[chunk_idx]); | 1150 | 13.9k | auto& chunk_info = m_set_info[chunk_idx]; | 1151 | | | 1152 | | // Remember the best dependency {par, chl} seen so far. | 1153 | 13.9k | std::pair<TxIdx, TxIdx> candidate_dep = {TxIdx(-1), TxIdx(-1)}; | 1154 | 13.9k | uint64_t candidate_tiebreak = 0; | 1155 | | // Iterate over all transactions. | 1156 | 24.9k | for (auto tx_idx : chunk_info.transactions) { Branch (1156:26): [True: 24.9k, False: 13.9k]
| 1157 | 24.9k | const auto& tx_data = m_tx_data[tx_idx]; | 1158 | | // Iterate over all active child dependencies of the transaction. | 1159 | 24.9k | for (auto child_idx : tx_data.active_children) { Branch (1159:33): [True: 10.9k, False: 24.9k]
| 1160 | 10.9k | auto& dep_top_info = m_set_info[tx_data.dep_top_idx[child_idx]]; | 1161 | | // Skip if this dependency is ineligible (the top chunk that would be created | 1162 | | // does not have higher feerate than the chunk it is currently part of). | 1163 | 10.9k | auto cmp = ByRatio{dep_top_info.feerate} <=> ByRatio{chunk_info.feerate}; | 1164 | 10.9k | if (cmp <= 0) continue; Branch (1164:21): [True: 10.3k, False: 601]
| 1165 | | // Generate a random tiebreak for this dependency, and reject it if its tiebreak | 1166 | | // is worse than the best so far. This means that among all eligible | 1167 | | // dependencies, a uniformly random one will be chosen. | 1168 | 601 | uint64_t tiebreak = m_rng.rand64(); | 1169 | 601 | if (tiebreak < candidate_tiebreak) continue; Branch (1169:21): [True: 251, False: 350]
| 1170 | | // Remember this as our (new) candidate dependency. | 1171 | 350 | candidate_dep = {tx_idx, child_idx}; | 1172 | 350 | candidate_tiebreak = tiebreak; | 1173 | 350 | } | 1174 | 24.9k | } | 1175 | 13.9k | m_cost.PickDependencyToSplitEnd(/*num_txns=*/chunk_info.transactions.Count()); | 1176 | 13.9k | return candidate_dep; | 1177 | 13.9k | } |
_ZN17cluster_linearize19SpanningForestStateIN13bitset_detail9IntBitSetImEENS_19SFLDefaultCostModelEE21PickDependencyToSplitEh Line | Count | Source | 1147 | 3.91M | { | 1148 | 3.91M | m_cost.PickDependencyToSplitBegin(); | 1149 | 3.91M | Assume(m_chunk_idxs[chunk_idx]); | 1150 | 3.91M | auto& chunk_info = m_set_info[chunk_idx]; | 1151 | | | 1152 | | // Remember the best dependency {par, chl} seen so far. | 1153 | 3.91M | std::pair<TxIdx, TxIdx> candidate_dep = {TxIdx(-1), TxIdx(-1)}; | 1154 | 3.91M | uint64_t candidate_tiebreak = 0; | 1155 | | // Iterate over all transactions. | 1156 | 9.46M | for (auto tx_idx : chunk_info.transactions) { Branch (1156:26): [True: 9.46M, False: 3.91M]
| 1157 | 9.46M | const auto& tx_data = m_tx_data[tx_idx]; | 1158 | | // Iterate over all active child dependencies of the transaction. | 1159 | 9.46M | for (auto child_idx : tx_data.active_children) { Branch (1159:33): [True: 5.54M, False: 9.46M]
| 1160 | 5.54M | auto& dep_top_info = m_set_info[tx_data.dep_top_idx[child_idx]]; | 1161 | | // Skip if this dependency is ineligible (the top chunk that would be created | 1162 | | // does not have higher feerate than the chunk it is currently part of). | 1163 | 5.54M | auto cmp = ByRatio{dep_top_info.feerate} <=> ByRatio{chunk_info.feerate}; | 1164 | 5.54M | if (cmp <= 0) continue; Branch (1164:21): [True: 4.10M, False: 1.44M]
| 1165 | | // Generate a random tiebreak for this dependency, and reject it if its tiebreak | 1166 | | // is worse than the best so far. This means that among all eligible | 1167 | | // dependencies, a uniformly random one will be chosen. | 1168 | 1.44M | uint64_t tiebreak = m_rng.rand64(); | 1169 | 1.44M | if (tiebreak < candidate_tiebreak) continue; Branch (1169:21): [True: 1.13M, False: 302k]
| 1170 | | // Remember this as our (new) candidate dependency. | 1171 | 302k | candidate_dep = {tx_idx, child_idx}; | 1172 | 302k | candidate_tiebreak = tiebreak; | 1173 | 302k | } | 1174 | 9.46M | } | 1175 | 3.91M | m_cost.PickDependencyToSplitEnd(/*num_txns=*/chunk_info.transactions.Count()); | 1176 | 3.91M | return candidate_dep; | 1177 | 3.91M | } |
|
1178 | | |
1179 | | public: |
1180 | | /** Construct a spanning forest for the given DepGraph, with every transaction in its own chunk |
1181 | | * (not topological). */ |
1182 | | explicit SpanningForestState(const DepGraph<SetType>& depgraph LIFETIMEBOUND, uint64_t rng_seed, const CostModel& cost = CostModel{}) noexcept : |
1183 | 1.32M | m_rng(rng_seed), m_depgraph(depgraph), m_cost(cost) |
1184 | 1.32M | { |
1185 | 1.32M | m_cost.InitializeBegin(); |
1186 | 1.32M | m_transaction_idxs = depgraph.Positions(); |
1187 | 1.32M | auto num_transactions = m_transaction_idxs.Count(); |
1188 | 1.32M | m_tx_data.resize(depgraph.PositionRange()); |
1189 | 1.32M | m_set_info.resize(num_transactions); |
1190 | 1.32M | m_reachable.resize(num_transactions); |
1191 | 1.32M | m_suboptimal_chunks.reserve(num_transactions); |
1192 | 1.32M | size_t num_chunks = 0; |
1193 | 1.32M | size_t num_deps = 0; |
1194 | 6.71M | for (auto tx_idx : m_transaction_idxs) { Branch (1194:26): [True: 27.4k, False: 1.24k]
Branch (1194:26): [True: 21.1k, False: 389]
Branch (1194:26): [True: 6.66M, False: 1.32M]
|
1195 | | // Fill in transaction data. |
1196 | 6.71M | auto& tx_data = m_tx_data[tx_idx]; |
1197 | 6.71M | tx_data.parents = depgraph.GetReducedParents(tx_idx); |
1198 | 6.71M | for (auto parent_idx : tx_data.parents) { Branch (1198:34): [True: 26.2k, False: 27.4k]
Branch (1198:34): [True: 9.89k, False: 21.1k]
Branch (1198:34): [True: 5.72M, False: 6.66M]
|
1199 | 5.76M | m_tx_data[parent_idx].children.Set(tx_idx); |
1200 | 5.76M | } |
1201 | 6.71M | num_deps += tx_data.parents.Count(); |
1202 | | // Create a singleton chunk for it. |
1203 | 6.71M | tx_data.chunk_idx = num_chunks; |
1204 | 6.71M | m_set_info[num_chunks++] = SetInfo(depgraph, tx_idx); |
1205 | 6.71M | } |
1206 | | // Set the reachable transactions for each chunk to the transactions' parents and children. |
1207 | 8.03M | for (SetIdx chunk_idx = 0; chunk_idx < num_transactions; ++chunk_idx) { Branch (1207:36): [True: 27.4k, False: 1.24k]
Branch (1207:36): [True: 21.1k, False: 389]
Branch (1207:36): [True: 6.66M, False: 1.32M]
|
1208 | 6.71M | auto& tx_data = m_tx_data[m_set_info[chunk_idx].transactions.First()]; |
1209 | 6.71M | m_reachable[chunk_idx].first = tx_data.parents; |
1210 | 6.71M | m_reachable[chunk_idx].second = tx_data.children; |
1211 | 6.71M | } |
1212 | 1.32M | Assume(num_chunks == num_transactions); |
1213 | | // Mark all chunk sets as chunks. |
1214 | 1.32M | m_chunk_idxs = SetType::Fill(num_chunks); |
1215 | 1.32M | m_cost.InitializeEnd(/*num_txns=*/num_chunks, /*num_deps=*/num_deps); |
1216 | 1.32M | } _ZN17cluster_linearize19SpanningForestStateIN13bitset_detail9IntBitSetIjEENS_19SFLDefaultCostModelEEC2ERKNS_8DepGraphIS3_EEmRKS4_ Line | Count | Source | 1183 | 1.24k | m_rng(rng_seed), m_depgraph(depgraph), m_cost(cost) | 1184 | 1.24k | { | 1185 | 1.24k | m_cost.InitializeBegin(); | 1186 | 1.24k | m_transaction_idxs = depgraph.Positions(); | 1187 | 1.24k | auto num_transactions = m_transaction_idxs.Count(); | 1188 | 1.24k | m_tx_data.resize(depgraph.PositionRange()); | 1189 | 1.24k | m_set_info.resize(num_transactions); | 1190 | 1.24k | m_reachable.resize(num_transactions); | 1191 | 1.24k | m_suboptimal_chunks.reserve(num_transactions); | 1192 | 1.24k | size_t num_chunks = 0; | 1193 | 1.24k | size_t num_deps = 0; | 1194 | 27.4k | for (auto tx_idx : m_transaction_idxs) { Branch (1194:26): [True: 27.4k, False: 1.24k]
| 1195 | | // Fill in transaction data. | 1196 | 27.4k | auto& tx_data = m_tx_data[tx_idx]; | 1197 | 27.4k | tx_data.parents = depgraph.GetReducedParents(tx_idx); | 1198 | 27.4k | for (auto parent_idx : tx_data.parents) { Branch (1198:34): [True: 26.2k, False: 27.4k]
| 1199 | 26.2k | m_tx_data[parent_idx].children.Set(tx_idx); | 1200 | 26.2k | } | 1201 | 27.4k | num_deps += tx_data.parents.Count(); | 1202 | | // Create a singleton chunk for it. | 1203 | 27.4k | tx_data.chunk_idx = num_chunks; | 1204 | 27.4k | m_set_info[num_chunks++] = SetInfo(depgraph, tx_idx); | 1205 | 27.4k | } | 1206 | | // Set the reachable transactions for each chunk to the transactions' parents and children. | 1207 | 28.6k | for (SetIdx chunk_idx = 0; chunk_idx < num_transactions; ++chunk_idx) { Branch (1207:36): [True: 27.4k, False: 1.24k]
| 1208 | 27.4k | auto& tx_data = m_tx_data[m_set_info[chunk_idx].transactions.First()]; | 1209 | 27.4k | m_reachable[chunk_idx].first = tx_data.parents; | 1210 | 27.4k | m_reachable[chunk_idx].second = tx_data.children; | 1211 | 27.4k | } | 1212 | 1.24k | Assume(num_chunks == num_transactions); | 1213 | | // Mark all chunk sets as chunks. | 1214 | 1.24k | m_chunk_idxs = SetType::Fill(num_chunks); | 1215 | 1.24k | m_cost.InitializeEnd(/*num_txns=*/num_chunks, /*num_deps=*/num_deps); | 1216 | 1.24k | } |
_ZN17cluster_linearize19SpanningForestStateIN13bitset_detail14MultiIntBitSetImLj2EEENS_19SFLDefaultCostModelEEC2ERKNS_8DepGraphIS3_EEmRKS4_ Line | Count | Source | 1183 | 389 | m_rng(rng_seed), m_depgraph(depgraph), m_cost(cost) | 1184 | 389 | { | 1185 | 389 | m_cost.InitializeBegin(); | 1186 | 389 | m_transaction_idxs = depgraph.Positions(); | 1187 | 389 | auto num_transactions = m_transaction_idxs.Count(); | 1188 | 389 | m_tx_data.resize(depgraph.PositionRange()); | 1189 | 389 | m_set_info.resize(num_transactions); | 1190 | 389 | m_reachable.resize(num_transactions); | 1191 | 389 | m_suboptimal_chunks.reserve(num_transactions); | 1192 | 389 | size_t num_chunks = 0; | 1193 | 389 | size_t num_deps = 0; | 1194 | 21.1k | for (auto tx_idx : m_transaction_idxs) { Branch (1194:26): [True: 21.1k, False: 389]
| 1195 | | // Fill in transaction data. | 1196 | 21.1k | auto& tx_data = m_tx_data[tx_idx]; | 1197 | 21.1k | tx_data.parents = depgraph.GetReducedParents(tx_idx); | 1198 | 21.1k | for (auto parent_idx : tx_data.parents) { Branch (1198:34): [True: 9.89k, False: 21.1k]
| 1199 | 9.89k | m_tx_data[parent_idx].children.Set(tx_idx); | 1200 | 9.89k | } | 1201 | 21.1k | num_deps += tx_data.parents.Count(); | 1202 | | // Create a singleton chunk for it. | 1203 | 21.1k | tx_data.chunk_idx = num_chunks; | 1204 | 21.1k | m_set_info[num_chunks++] = SetInfo(depgraph, tx_idx); | 1205 | 21.1k | } | 1206 | | // Set the reachable transactions for each chunk to the transactions' parents and children. | 1207 | 21.5k | for (SetIdx chunk_idx = 0; chunk_idx < num_transactions; ++chunk_idx) { Branch (1207:36): [True: 21.1k, False: 389]
| 1208 | 21.1k | auto& tx_data = m_tx_data[m_set_info[chunk_idx].transactions.First()]; | 1209 | 21.1k | m_reachable[chunk_idx].first = tx_data.parents; | 1210 | 21.1k | m_reachable[chunk_idx].second = tx_data.children; | 1211 | 21.1k | } | 1212 | 389 | Assume(num_chunks == num_transactions); | 1213 | | // Mark all chunk sets as chunks. | 1214 | 389 | m_chunk_idxs = SetType::Fill(num_chunks); | 1215 | 389 | m_cost.InitializeEnd(/*num_txns=*/num_chunks, /*num_deps=*/num_deps); | 1216 | 389 | } |
_ZN17cluster_linearize19SpanningForestStateIN13bitset_detail9IntBitSetImEENS_19SFLDefaultCostModelEEC2ERKNS_8DepGraphIS3_EEmRKS4_ Line | Count | Source | 1183 | 1.32M | m_rng(rng_seed), m_depgraph(depgraph), m_cost(cost) | 1184 | 1.32M | { | 1185 | 1.32M | m_cost.InitializeBegin(); | 1186 | 1.32M | m_transaction_idxs = depgraph.Positions(); | 1187 | 1.32M | auto num_transactions = m_transaction_idxs.Count(); | 1188 | 1.32M | m_tx_data.resize(depgraph.PositionRange()); | 1189 | 1.32M | m_set_info.resize(num_transactions); | 1190 | 1.32M | m_reachable.resize(num_transactions); | 1191 | 1.32M | m_suboptimal_chunks.reserve(num_transactions); | 1192 | 1.32M | size_t num_chunks = 0; | 1193 | 1.32M | size_t num_deps = 0; | 1194 | 6.66M | for (auto tx_idx : m_transaction_idxs) { Branch (1194:26): [True: 6.66M, False: 1.32M]
| 1195 | | // Fill in transaction data. | 1196 | 6.66M | auto& tx_data = m_tx_data[tx_idx]; | 1197 | 6.66M | tx_data.parents = depgraph.GetReducedParents(tx_idx); | 1198 | 6.66M | for (auto parent_idx : tx_data.parents) { Branch (1198:34): [True: 5.72M, False: 6.66M]
| 1199 | 5.72M | m_tx_data[parent_idx].children.Set(tx_idx); | 1200 | 5.72M | } | 1201 | 6.66M | num_deps += tx_data.parents.Count(); | 1202 | | // Create a singleton chunk for it. | 1203 | 6.66M | tx_data.chunk_idx = num_chunks; | 1204 | 6.66M | m_set_info[num_chunks++] = SetInfo(depgraph, tx_idx); | 1205 | 6.66M | } | 1206 | | // Set the reachable transactions for each chunk to the transactions' parents and children. | 1207 | 7.98M | for (SetIdx chunk_idx = 0; chunk_idx < num_transactions; ++chunk_idx) { Branch (1207:36): [True: 6.66M, False: 1.32M]
| 1208 | 6.66M | auto& tx_data = m_tx_data[m_set_info[chunk_idx].transactions.First()]; | 1209 | 6.66M | m_reachable[chunk_idx].first = tx_data.parents; | 1210 | 6.66M | m_reachable[chunk_idx].second = tx_data.children; | 1211 | 6.66M | } | 1212 | 1.32M | Assume(num_chunks == num_transactions); | 1213 | | // Mark all chunk sets as chunks. | 1214 | 1.32M | m_chunk_idxs = SetType::Fill(num_chunks); | 1215 | 1.32M | m_cost.InitializeEnd(/*num_txns=*/num_chunks, /*num_deps=*/num_deps); | 1216 | 1.32M | } |
|
1217 | | |
1218 | | /** Load an existing linearization. Must be called immediately after constructor. The result is |
1219 | | * topological if the linearization is valid. Otherwise, MakeTopological still needs to be |
1220 | | * called. */ |
1221 | | void LoadLinearization(std::span<const DepGraphIndex> old_linearization) noexcept |
1222 | 1.32M | { |
1223 | | // Add transactions one by one, in order of existing linearization. |
1224 | 6.69M | for (DepGraphIndex tx_idx : old_linearization) { Branch (1224:35): [True: 14.0k, False: 670]
Branch (1224:35): [True: 21.1k, False: 362]
Branch (1224:35): [True: 6.66M, False: 1.32M]
|
1225 | 6.69M | auto chunk_idx = m_tx_data[tx_idx].chunk_idx; |
1226 | | // Merge the chunk upwards, as long as merging succeeds. |
1227 | 9.56M | while (true) { Branch (1227:20): [Folded - Ignored]
Branch (1227:20): [Folded - Ignored]
Branch (1227:20): [Folded - Ignored]
|
1228 | 9.56M | chunk_idx = MergeStep<false>(chunk_idx); |
1229 | 9.56M | if (chunk_idx == INVALID_SET_IDX) break; Branch (1229:21): [True: 14.0k, False: 9.86k]
Branch (1229:21): [True: 21.1k, False: 7.37k]
Branch (1229:21): [True: 6.66M, False: 2.84M]
|
1230 | 9.56M | } |
1231 | 6.69M | } |
1232 | 1.32M | } _ZN17cluster_linearize19SpanningForestStateIN13bitset_detail9IntBitSetIjEENS_19SFLDefaultCostModelEE17LoadLinearizationESt4spanIKjLm18446744073709551615EE Line | Count | Source | 1222 | 670 | { | 1223 | | // Add transactions one by one, in order of existing linearization. | 1224 | 14.0k | for (DepGraphIndex tx_idx : old_linearization) { Branch (1224:35): [True: 14.0k, False: 670]
| 1225 | 14.0k | auto chunk_idx = m_tx_data[tx_idx].chunk_idx; | 1226 | | // Merge the chunk upwards, as long as merging succeeds. | 1227 | 23.9k | while (true) { Branch (1227:20): [Folded - Ignored]
| 1228 | 23.9k | chunk_idx = MergeStep<false>(chunk_idx); | 1229 | 23.9k | if (chunk_idx == INVALID_SET_IDX) break; Branch (1229:21): [True: 14.0k, False: 9.86k]
| 1230 | 23.9k | } | 1231 | 14.0k | } | 1232 | 670 | } |
_ZN17cluster_linearize19SpanningForestStateIN13bitset_detail14MultiIntBitSetImLj2EEENS_19SFLDefaultCostModelEE17LoadLinearizationESt4spanIKjLm18446744073709551615EE Line | Count | Source | 1222 | 362 | { | 1223 | | // Add transactions one by one, in order of existing linearization. | 1224 | 21.1k | for (DepGraphIndex tx_idx : old_linearization) { Branch (1224:35): [True: 21.1k, False: 362]
| 1225 | 21.1k | auto chunk_idx = m_tx_data[tx_idx].chunk_idx; | 1226 | | // Merge the chunk upwards, as long as merging succeeds. | 1227 | 28.5k | while (true) { Branch (1227:20): [Folded - Ignored]
| 1228 | 28.5k | chunk_idx = MergeStep<false>(chunk_idx); | 1229 | 28.5k | if (chunk_idx == INVALID_SET_IDX) break; Branch (1229:21): [True: 21.1k, False: 7.37k]
| 1230 | 28.5k | } | 1231 | 21.1k | } | 1232 | 362 | } |
_ZN17cluster_linearize19SpanningForestStateIN13bitset_detail9IntBitSetImEENS_19SFLDefaultCostModelEE17LoadLinearizationESt4spanIKjLm18446744073709551615EE Line | Count | Source | 1222 | 1.32M | { | 1223 | | // Add transactions one by one, in order of existing linearization. | 1224 | 6.66M | for (DepGraphIndex tx_idx : old_linearization) { Branch (1224:35): [True: 6.66M, False: 1.32M]
| 1225 | 6.66M | auto chunk_idx = m_tx_data[tx_idx].chunk_idx; | 1226 | | // Merge the chunk upwards, as long as merging succeeds. | 1227 | 9.50M | while (true) { Branch (1227:20): [Folded - Ignored]
| 1228 | 9.50M | chunk_idx = MergeStep<false>(chunk_idx); | 1229 | 9.50M | if (chunk_idx == INVALID_SET_IDX) break; Branch (1229:21): [True: 6.66M, False: 2.84M]
| 1230 | 9.50M | } | 1231 | 6.66M | } | 1232 | 1.32M | } |
|
1233 | | |
1234 | | /** Make state topological. Can be called after constructing, or after LoadLinearization. */ |
1235 | | void MakeTopological() noexcept |
1236 | 773k | { |
1237 | 773k | m_cost.MakeTopologicalBegin(); |
1238 | 773k | Assume(m_suboptimal_chunks.empty()); |
1239 | | /** What direction to initially merge chunks in; one of the two directions is enough. This |
1240 | | * is sufficient because if a non-topological inactive dependency exists between two |
1241 | | * chunks, at least one of the two chunks will eventually be processed in a direction that |
1242 | | * discovers it - either the lower chunk tries upward, or the upper chunk tries downward. |
1243 | | * Chunks that are the result of the merging are always tried in both directions. */ |
1244 | 773k | unsigned init_dir = m_rng.randbool(); |
1245 | | /** Which chunks are the result of merging, and thus need merge attempts in both |
1246 | | * directions. */ |
1247 | 773k | SetType merged_chunks; |
1248 | | // Mark chunks as suboptimal. |
1249 | 773k | m_suboptimal_idxs = m_chunk_idxs; |
1250 | 2.53M | for (auto chunk_idx : m_chunk_idxs) { Branch (1250:29): [True: 14.4k, False: 722]
Branch (1250:29): [True: 0, False: 27]
Branch (1250:29): [True: 2.52M, False: 772k]
|
1251 | 2.53M | m_suboptimal_chunks.emplace_back(chunk_idx); |
1252 | | // Randomize the initial order of suboptimal chunks in the queue. |
1253 | 2.53M | SetIdx j = m_rng.randrange<SetIdx>(m_suboptimal_chunks.size()); |
1254 | 2.53M | if (j != m_suboptimal_chunks.size() - 1) { Branch (1254:17): [True: 12.4k, False: 2.05k]
Branch (1254:17): [True: 0, False: 0]
Branch (1254:17): [True: 1.42M, False: 1.09M]
|
1255 | 1.44M | std::swap(m_suboptimal_chunks.back(), m_suboptimal_chunks[j]); |
1256 | 1.44M | } |
1257 | 2.53M | } |
1258 | 773k | unsigned chunks = m_chunk_idxs.Count(); |
1259 | 773k | unsigned steps = 0; |
1260 | 3.32M | while (!m_suboptimal_chunks.empty()) { Branch (1260:16): [True: 20.6k, False: 722]
Branch (1260:16): [True: 0, False: 27]
Branch (1260:16): [True: 2.52M, False: 772k]
|
1261 | 2.54M | ++steps; |
1262 | | // Pop an entry from the potentially-suboptimal chunk queue. |
1263 | 2.54M | SetIdx chunk_idx = m_suboptimal_chunks.front(); |
1264 | 2.54M | m_suboptimal_chunks.pop_front(); |
1265 | 2.54M | Assume(m_suboptimal_idxs[chunk_idx]); |
1266 | 2.54M | m_suboptimal_idxs.Reset(chunk_idx); |
1267 | | // If what was popped is not currently a chunk, continue. This may |
1268 | | // happen when it was merged with something else since being added. |
1269 | 2.54M | if (!m_chunk_idxs[chunk_idx]) continue; Branch (1269:17): [True: 3.56k, False: 17.1k]
Branch (1269:17): [True: 0, False: 0]
Branch (1269:17): [True: 2.03k, False: 2.52M]
|
1270 | | /** What direction(s) to attempt merging in. 1=up, 2=down, 3=both. */ |
1271 | 2.54M | unsigned direction = merged_chunks[chunk_idx] ? 3 : init_dir + 1; Branch (1271:34): [True: 5.09k, False: 12.0k]
Branch (1271:34): [True: 0, False: 0]
Branch (1271:34): [True: 5.87k, False: 2.51M]
|
1272 | 2.54M | int flip = m_rng.randbool(); |
1273 | 7.60M | for (int i = 0; i < 2; ++i) { Branch (1273:29): [True: 29.3k, False: 8.26k]
Branch (1273:29): [True: 0, False: 0]
Branch (1273:29): [True: 5.04M, False: 2.51M]
|
1274 | 5.07M | if (i ^ flip) { Branch (1274:21): [True: 15.0k, False: 14.2k]
Branch (1274:21): [True: 0, False: 0]
Branch (1274:21): [True: 2.52M, False: 2.52M]
|
1275 | 2.53M | if (!(direction & 1)) continue; Branch (1275:25): [True: 4.21k, False: 10.8k]
Branch (1275:25): [True: 0, False: 0]
Branch (1275:25): [True: 1.24M, False: 1.28M]
|
1276 | | // Attempt to merge the chunk upwards. |
1277 | 1.29M | auto result_up = MergeStep<false>(chunk_idx); |
1278 | 1.29M | if (result_up != INVALID_SET_IDX) { Branch (1278:25): [True: 5.25k, False: 5.61k]
Branch (1278:25): [True: 0, False: 0]
Branch (1278:25): [True: 3.18k, False: 1.27M]
|
1279 | 8.43k | if (!m_suboptimal_idxs[result_up]) { Branch (1279:29): [True: 5.25k, False: 0]
Branch (1279:29): [True: 0, False: 0]
Branch (1279:29): [True: 3.18k, False: 0]
|
1280 | 8.43k | m_suboptimal_idxs.Set(result_up); |
1281 | 8.43k | m_suboptimal_chunks.push_back(result_up); |
1282 | 8.43k | } |
1283 | 8.43k | merged_chunks.Set(result_up); |
1284 | 8.43k | break; |
1285 | 8.43k | } |
1286 | 2.53M | } else { |
1287 | 2.53M | if (!(direction & 2)) continue; Branch (1287:25): [True: 5.37k, False: 8.84k]
Branch (1287:25): [True: 0, False: 0]
Branch (1287:25): [True: 1.27M, False: 1.24M]
|
1288 | | // Attempt to merge the chunk downwards. |
1289 | 1.25M | auto result_down = MergeStep<true>(chunk_idx); |
1290 | 1.25M | if (result_down != INVALID_SET_IDX) { Branch (1290:25): [True: 3.60k, False: 5.24k]
Branch (1290:25): [True: 0, False: 0]
Branch (1290:25): [True: 3.95k, False: 1.24M]
|
1291 | 7.55k | if (!m_suboptimal_idxs[result_down]) { Branch (1291:29): [True: 973, False: 2.62k]
Branch (1291:29): [True: 0, False: 0]
Branch (1291:29): [True: 2.80k, False: 1.14k]
|
1292 | 3.77k | m_suboptimal_idxs.Set(result_down); |
1293 | 3.77k | m_suboptimal_chunks.push_back(result_down); |
1294 | 3.77k | } |
1295 | 7.55k | merged_chunks.Set(result_down); |
1296 | 7.55k | break; |
1297 | 7.55k | } |
1298 | 1.25M | } |
1299 | 5.07M | } |
1300 | 2.54M | } |
1301 | 773k | m_cost.MakeTopologicalEnd(/*num_chunks=*/chunks, /*num_steps=*/steps); |
1302 | 773k | } _ZN17cluster_linearize19SpanningForestStateIN13bitset_detail9IntBitSetIjEENS_19SFLDefaultCostModelEE15MakeTopologicalEv Line | Count | Source | 1236 | 722 | { | 1237 | 722 | m_cost.MakeTopologicalBegin(); | 1238 | 722 | Assume(m_suboptimal_chunks.empty()); | 1239 | | /** What direction to initially merge chunks in; one of the two directions is enough. This | 1240 | | * is sufficient because if a non-topological inactive dependency exists between two | 1241 | | * chunks, at least one of the two chunks will eventually be processed in a direction that | 1242 | | * discovers it - either the lower chunk tries upward, or the upper chunk tries downward. | 1243 | | * Chunks that are the result of the merging are always tried in both directions. */ | 1244 | 722 | unsigned init_dir = m_rng.randbool(); | 1245 | | /** Which chunks are the result of merging, and thus need merge attempts in both | 1246 | | * directions. */ | 1247 | 722 | SetType merged_chunks; | 1248 | | // Mark chunks as suboptimal. | 1249 | 722 | m_suboptimal_idxs = m_chunk_idxs; | 1250 | 14.4k | for (auto chunk_idx : m_chunk_idxs) { Branch (1250:29): [True: 14.4k, False: 722]
| 1251 | 14.4k | m_suboptimal_chunks.emplace_back(chunk_idx); | 1252 | | // Randomize the initial order of suboptimal chunks in the queue. | 1253 | 14.4k | SetIdx j = m_rng.randrange<SetIdx>(m_suboptimal_chunks.size()); | 1254 | 14.4k | if (j != m_suboptimal_chunks.size() - 1) { Branch (1254:17): [True: 12.4k, False: 2.05k]
| 1255 | 12.4k | std::swap(m_suboptimal_chunks.back(), m_suboptimal_chunks[j]); | 1256 | 12.4k | } | 1257 | 14.4k | } | 1258 | 722 | unsigned chunks = m_chunk_idxs.Count(); | 1259 | 722 | unsigned steps = 0; | 1260 | 21.4k | while (!m_suboptimal_chunks.empty()) { Branch (1260:16): [True: 20.6k, False: 722]
| 1261 | 20.6k | ++steps; | 1262 | | // Pop an entry from the potentially-suboptimal chunk queue. | 1263 | 20.6k | SetIdx chunk_idx = m_suboptimal_chunks.front(); | 1264 | 20.6k | m_suboptimal_chunks.pop_front(); | 1265 | 20.6k | Assume(m_suboptimal_idxs[chunk_idx]); | 1266 | 20.6k | m_suboptimal_idxs.Reset(chunk_idx); | 1267 | | // If what was popped is not currently a chunk, continue. This may | 1268 | | // happen when it was merged with something else since being added. | 1269 | 20.6k | if (!m_chunk_idxs[chunk_idx]) continue; Branch (1269:17): [True: 3.56k, False: 17.1k]
| 1270 | | /** What direction(s) to attempt merging in. 1=up, 2=down, 3=both. */ | 1271 | 17.1k | unsigned direction = merged_chunks[chunk_idx] ? 3 : init_dir + 1; Branch (1271:34): [True: 5.09k, False: 12.0k]
| 1272 | 17.1k | int flip = m_rng.randbool(); | 1273 | 37.5k | for (int i = 0; i < 2; ++i) { Branch (1273:29): [True: 29.3k, False: 8.26k]
| 1274 | 29.3k | if (i ^ flip) { Branch (1274:21): [True: 15.0k, False: 14.2k]
| 1275 | 15.0k | if (!(direction & 1)) continue; Branch (1275:25): [True: 4.21k, False: 10.8k]
| 1276 | | // Attempt to merge the chunk upwards. | 1277 | 10.8k | auto result_up = MergeStep<false>(chunk_idx); | 1278 | 10.8k | if (result_up != INVALID_SET_IDX) { Branch (1278:25): [True: 5.25k, False: 5.61k]
| 1279 | 5.25k | if (!m_suboptimal_idxs[result_up]) { Branch (1279:29): [True: 5.25k, False: 0]
| 1280 | 5.25k | m_suboptimal_idxs.Set(result_up); | 1281 | 5.25k | m_suboptimal_chunks.push_back(result_up); | 1282 | 5.25k | } | 1283 | 5.25k | merged_chunks.Set(result_up); | 1284 | 5.25k | break; | 1285 | 5.25k | } | 1286 | 14.2k | } else { | 1287 | 14.2k | if (!(direction & 2)) continue; Branch (1287:25): [True: 5.37k, False: 8.84k]
| 1288 | | // Attempt to merge the chunk downwards. | 1289 | 8.84k | auto result_down = MergeStep<true>(chunk_idx); | 1290 | 8.84k | if (result_down != INVALID_SET_IDX) { Branch (1290:25): [True: 3.60k, False: 5.24k]
| 1291 | 3.60k | if (!m_suboptimal_idxs[result_down]) { Branch (1291:29): [True: 973, False: 2.62k]
| 1292 | 973 | m_suboptimal_idxs.Set(result_down); | 1293 | 973 | m_suboptimal_chunks.push_back(result_down); | 1294 | 973 | } | 1295 | 3.60k | merged_chunks.Set(result_down); | 1296 | 3.60k | break; | 1297 | 3.60k | } | 1298 | 8.84k | } | 1299 | 29.3k | } | 1300 | 17.1k | } | 1301 | 722 | m_cost.MakeTopologicalEnd(/*num_chunks=*/chunks, /*num_steps=*/steps); | 1302 | 722 | } |
_ZN17cluster_linearize19SpanningForestStateIN13bitset_detail14MultiIntBitSetImLj2EEENS_19SFLDefaultCostModelEE15MakeTopologicalEv Line | Count | Source | 1236 | 27 | { | 1237 | 27 | m_cost.MakeTopologicalBegin(); | 1238 | 27 | Assume(m_suboptimal_chunks.empty()); | 1239 | | /** What direction to initially merge chunks in; one of the two directions is enough. This | 1240 | | * is sufficient because if a non-topological inactive dependency exists between two | 1241 | | * chunks, at least one of the two chunks will eventually be processed in a direction that | 1242 | | * discovers it - either the lower chunk tries upward, or the upper chunk tries downward. | 1243 | | * Chunks that are the result of the merging are always tried in both directions. */ | 1244 | 27 | unsigned init_dir = m_rng.randbool(); | 1245 | | /** Which chunks are the result of merging, and thus need merge attempts in both | 1246 | | * directions. */ | 1247 | 27 | SetType merged_chunks; | 1248 | | // Mark chunks as suboptimal. | 1249 | 27 | m_suboptimal_idxs = m_chunk_idxs; | 1250 | 27 | for (auto chunk_idx : m_chunk_idxs) { Branch (1250:29): [True: 0, False: 27]
| 1251 | 0 | m_suboptimal_chunks.emplace_back(chunk_idx); | 1252 | | // Randomize the initial order of suboptimal chunks in the queue. | 1253 | 0 | SetIdx j = m_rng.randrange<SetIdx>(m_suboptimal_chunks.size()); | 1254 | 0 | if (j != m_suboptimal_chunks.size() - 1) { Branch (1254:17): [True: 0, False: 0]
| 1255 | 0 | std::swap(m_suboptimal_chunks.back(), m_suboptimal_chunks[j]); | 1256 | 0 | } | 1257 | 0 | } | 1258 | 27 | unsigned chunks = m_chunk_idxs.Count(); | 1259 | 27 | unsigned steps = 0; | 1260 | 27 | while (!m_suboptimal_chunks.empty()) { Branch (1260:16): [True: 0, False: 27]
| 1261 | 0 | ++steps; | 1262 | | // Pop an entry from the potentially-suboptimal chunk queue. | 1263 | 0 | SetIdx chunk_idx = m_suboptimal_chunks.front(); | 1264 | 0 | m_suboptimal_chunks.pop_front(); | 1265 | 0 | Assume(m_suboptimal_idxs[chunk_idx]); | 1266 | 0 | m_suboptimal_idxs.Reset(chunk_idx); | 1267 | | // If what was popped is not currently a chunk, continue. This may | 1268 | | // happen when it was merged with something else since being added. | 1269 | 0 | if (!m_chunk_idxs[chunk_idx]) continue; Branch (1269:17): [True: 0, False: 0]
| 1270 | | /** What direction(s) to attempt merging in. 1=up, 2=down, 3=both. */ | 1271 | 0 | unsigned direction = merged_chunks[chunk_idx] ? 3 : init_dir + 1; Branch (1271:34): [True: 0, False: 0]
| 1272 | 0 | int flip = m_rng.randbool(); | 1273 | 0 | for (int i = 0; i < 2; ++i) { Branch (1273:29): [True: 0, False: 0]
| 1274 | 0 | if (i ^ flip) { Branch (1274:21): [True: 0, False: 0]
| 1275 | 0 | if (!(direction & 1)) continue; Branch (1275:25): [True: 0, False: 0]
| 1276 | | // Attempt to merge the chunk upwards. | 1277 | 0 | auto result_up = MergeStep<false>(chunk_idx); | 1278 | 0 | if (result_up != INVALID_SET_IDX) { Branch (1278:25): [True: 0, False: 0]
| 1279 | 0 | if (!m_suboptimal_idxs[result_up]) { Branch (1279:29): [True: 0, False: 0]
| 1280 | 0 | m_suboptimal_idxs.Set(result_up); | 1281 | 0 | m_suboptimal_chunks.push_back(result_up); | 1282 | 0 | } | 1283 | 0 | merged_chunks.Set(result_up); | 1284 | 0 | break; | 1285 | 0 | } | 1286 | 0 | } else { | 1287 | 0 | if (!(direction & 2)) continue; Branch (1287:25): [True: 0, False: 0]
| 1288 | | // Attempt to merge the chunk downwards. | 1289 | 0 | auto result_down = MergeStep<true>(chunk_idx); | 1290 | 0 | if (result_down != INVALID_SET_IDX) { Branch (1290:25): [True: 0, False: 0]
| 1291 | 0 | if (!m_suboptimal_idxs[result_down]) { Branch (1291:29): [True: 0, False: 0]
| 1292 | 0 | m_suboptimal_idxs.Set(result_down); | 1293 | 0 | m_suboptimal_chunks.push_back(result_down); | 1294 | 0 | } | 1295 | 0 | merged_chunks.Set(result_down); | 1296 | 0 | break; | 1297 | 0 | } | 1298 | 0 | } | 1299 | 0 | } | 1300 | 0 | } | 1301 | 27 | m_cost.MakeTopologicalEnd(/*num_chunks=*/chunks, /*num_steps=*/steps); | 1302 | 27 | } |
_ZN17cluster_linearize19SpanningForestStateIN13bitset_detail9IntBitSetImEENS_19SFLDefaultCostModelEE15MakeTopologicalEv Line | Count | Source | 1236 | 772k | { | 1237 | 772k | m_cost.MakeTopologicalBegin(); | 1238 | 772k | Assume(m_suboptimal_chunks.empty()); | 1239 | | /** What direction to initially merge chunks in; one of the two directions is enough. This | 1240 | | * is sufficient because if a non-topological inactive dependency exists between two | 1241 | | * chunks, at least one of the two chunks will eventually be processed in a direction that | 1242 | | * discovers it - either the lower chunk tries upward, or the upper chunk tries downward. | 1243 | | * Chunks that are the result of the merging are always tried in both directions. */ | 1244 | 772k | unsigned init_dir = m_rng.randbool(); | 1245 | | /** Which chunks are the result of merging, and thus need merge attempts in both | 1246 | | * directions. */ | 1247 | 772k | SetType merged_chunks; | 1248 | | // Mark chunks as suboptimal. | 1249 | 772k | m_suboptimal_idxs = m_chunk_idxs; | 1250 | 2.52M | for (auto chunk_idx : m_chunk_idxs) { Branch (1250:29): [True: 2.52M, False: 772k]
| 1251 | 2.52M | m_suboptimal_chunks.emplace_back(chunk_idx); | 1252 | | // Randomize the initial order of suboptimal chunks in the queue. | 1253 | 2.52M | SetIdx j = m_rng.randrange<SetIdx>(m_suboptimal_chunks.size()); | 1254 | 2.52M | if (j != m_suboptimal_chunks.size() - 1) { Branch (1254:17): [True: 1.42M, False: 1.09M]
| 1255 | 1.42M | std::swap(m_suboptimal_chunks.back(), m_suboptimal_chunks[j]); | 1256 | 1.42M | } | 1257 | 2.52M | } | 1258 | 772k | unsigned chunks = m_chunk_idxs.Count(); | 1259 | 772k | unsigned steps = 0; | 1260 | 3.30M | while (!m_suboptimal_chunks.empty()) { Branch (1260:16): [True: 2.52M, False: 772k]
| 1261 | 2.52M | ++steps; | 1262 | | // Pop an entry from the potentially-suboptimal chunk queue. | 1263 | 2.52M | SetIdx chunk_idx = m_suboptimal_chunks.front(); | 1264 | 2.52M | m_suboptimal_chunks.pop_front(); | 1265 | 2.52M | Assume(m_suboptimal_idxs[chunk_idx]); | 1266 | 2.52M | m_suboptimal_idxs.Reset(chunk_idx); | 1267 | | // If what was popped is not currently a chunk, continue. This may | 1268 | | // happen when it was merged with something else since being added. | 1269 | 2.52M | if (!m_chunk_idxs[chunk_idx]) continue; Branch (1269:17): [True: 2.03k, False: 2.52M]
| 1270 | | /** What direction(s) to attempt merging in. 1=up, 2=down, 3=both. */ | 1271 | 2.52M | unsigned direction = merged_chunks[chunk_idx] ? 3 : init_dir + 1; Branch (1271:34): [True: 5.87k, False: 2.51M]
| 1272 | 2.52M | int flip = m_rng.randbool(); | 1273 | 7.56M | for (int i = 0; i < 2; ++i) { Branch (1273:29): [True: 5.04M, False: 2.51M]
| 1274 | 5.04M | if (i ^ flip) { Branch (1274:21): [True: 2.52M, False: 2.52M]
| 1275 | 2.52M | if (!(direction & 1)) continue; Branch (1275:25): [True: 1.24M, False: 1.28M]
| 1276 | | // Attempt to merge the chunk upwards. | 1277 | 1.28M | auto result_up = MergeStep<false>(chunk_idx); | 1278 | 1.28M | if (result_up != INVALID_SET_IDX) { Branch (1278:25): [True: 3.18k, False: 1.27M]
| 1279 | 3.18k | if (!m_suboptimal_idxs[result_up]) { Branch (1279:29): [True: 3.18k, False: 0]
| 1280 | 3.18k | m_suboptimal_idxs.Set(result_up); | 1281 | 3.18k | m_suboptimal_chunks.push_back(result_up); | 1282 | 3.18k | } | 1283 | 3.18k | merged_chunks.Set(result_up); | 1284 | 3.18k | break; | 1285 | 3.18k | } | 1286 | 2.52M | } else { | 1287 | 2.52M | if (!(direction & 2)) continue; Branch (1287:25): [True: 1.27M, False: 1.24M]
| 1288 | | // Attempt to merge the chunk downwards. | 1289 | 1.24M | auto result_down = MergeStep<true>(chunk_idx); | 1290 | 1.24M | if (result_down != INVALID_SET_IDX) { Branch (1290:25): [True: 3.95k, False: 1.24M]
| 1291 | 3.95k | if (!m_suboptimal_idxs[result_down]) { Branch (1291:29): [True: 2.80k, False: 1.14k]
| 1292 | 2.80k | m_suboptimal_idxs.Set(result_down); | 1293 | 2.80k | m_suboptimal_chunks.push_back(result_down); | 1294 | 2.80k | } | 1295 | 3.95k | merged_chunks.Set(result_down); | 1296 | 3.95k | break; | 1297 | 3.95k | } | 1298 | 1.24M | } | 1299 | 5.04M | } | 1300 | 2.52M | } | 1301 | 772k | m_cost.MakeTopologicalEnd(/*num_chunks=*/chunks, /*num_steps=*/steps); | 1302 | 772k | } |
|
1303 | | |
1304 | | /** Initialize the data structure for optimization. It must be topological already. */ |
1305 | | void StartOptimizing() noexcept |
1306 | 1.30M | { |
1307 | 1.30M | m_cost.StartOptimizingBegin(); |
1308 | 1.30M | Assume(m_suboptimal_chunks.empty()); |
1309 | | // Mark chunks suboptimal. |
1310 | 1.30M | m_suboptimal_idxs = m_chunk_idxs; |
1311 | 3.73M | for (auto chunk_idx : m_chunk_idxs) { Branch (1311:29): [True: 8.44k, False: 1.18k]
Branch (1311:29): [True: 13.7k, False: 389]
Branch (1311:29): [True: 3.71M, False: 1.30M]
|
1312 | 3.73M | m_suboptimal_chunks.push_back(chunk_idx); |
1313 | | // Randomize the initial order of suboptimal chunks in the queue. |
1314 | 3.73M | SetIdx j = m_rng.randrange<SetIdx>(m_suboptimal_chunks.size()); |
1315 | 3.73M | if (j != m_suboptimal_chunks.size() - 1) { Branch (1315:17): [True: 5.92k, False: 2.52k]
Branch (1315:17): [True: 12.4k, False: 1.37k]
Branch (1315:17): [True: 1.85M, False: 1.85M]
|
1316 | 1.87M | std::swap(m_suboptimal_chunks.back(), m_suboptimal_chunks[j]); |
1317 | 1.87M | } |
1318 | 3.73M | } |
1319 | 1.30M | m_cost.StartOptimizingEnd(/*num_chunks=*/m_suboptimal_chunks.size()); |
1320 | 1.30M | } _ZN17cluster_linearize19SpanningForestStateIN13bitset_detail9IntBitSetIjEENS_19SFLDefaultCostModelEE15StartOptimizingEv Line | Count | Source | 1306 | 1.18k | { | 1307 | 1.18k | m_cost.StartOptimizingBegin(); | 1308 | 1.18k | Assume(m_suboptimal_chunks.empty()); | 1309 | | // Mark chunks suboptimal. | 1310 | 1.18k | m_suboptimal_idxs = m_chunk_idxs; | 1311 | 8.44k | for (auto chunk_idx : m_chunk_idxs) { Branch (1311:29): [True: 8.44k, False: 1.18k]
| 1312 | 8.44k | m_suboptimal_chunks.push_back(chunk_idx); | 1313 | | // Randomize the initial order of suboptimal chunks in the queue. | 1314 | 8.44k | SetIdx j = m_rng.randrange<SetIdx>(m_suboptimal_chunks.size()); | 1315 | 8.44k | if (j != m_suboptimal_chunks.size() - 1) { Branch (1315:17): [True: 5.92k, False: 2.52k]
| 1316 | 5.92k | std::swap(m_suboptimal_chunks.back(), m_suboptimal_chunks[j]); | 1317 | 5.92k | } | 1318 | 8.44k | } | 1319 | 1.18k | m_cost.StartOptimizingEnd(/*num_chunks=*/m_suboptimal_chunks.size()); | 1320 | 1.18k | } |
_ZN17cluster_linearize19SpanningForestStateIN13bitset_detail14MultiIntBitSetImLj2EEENS_19SFLDefaultCostModelEE15StartOptimizingEv Line | Count | Source | 1306 | 389 | { | 1307 | 389 | m_cost.StartOptimizingBegin(); | 1308 | 389 | Assume(m_suboptimal_chunks.empty()); | 1309 | | // Mark chunks suboptimal. | 1310 | 389 | m_suboptimal_idxs = m_chunk_idxs; | 1311 | 13.7k | for (auto chunk_idx : m_chunk_idxs) { Branch (1311:29): [True: 13.7k, False: 389]
| 1312 | 13.7k | m_suboptimal_chunks.push_back(chunk_idx); | 1313 | | // Randomize the initial order of suboptimal chunks in the queue. | 1314 | 13.7k | SetIdx j = m_rng.randrange<SetIdx>(m_suboptimal_chunks.size()); | 1315 | 13.7k | if (j != m_suboptimal_chunks.size() - 1) { Branch (1315:17): [True: 12.4k, False: 1.37k]
| 1316 | 12.4k | std::swap(m_suboptimal_chunks.back(), m_suboptimal_chunks[j]); | 1317 | 12.4k | } | 1318 | 13.7k | } | 1319 | 389 | m_cost.StartOptimizingEnd(/*num_chunks=*/m_suboptimal_chunks.size()); | 1320 | 389 | } |
_ZN17cluster_linearize19SpanningForestStateIN13bitset_detail9IntBitSetImEENS_19SFLDefaultCostModelEE15StartOptimizingEv Line | Count | Source | 1306 | 1.30M | { | 1307 | 1.30M | m_cost.StartOptimizingBegin(); | 1308 | 1.30M | Assume(m_suboptimal_chunks.empty()); | 1309 | | // Mark chunks suboptimal. | 1310 | 1.30M | m_suboptimal_idxs = m_chunk_idxs; | 1311 | 3.71M | for (auto chunk_idx : m_chunk_idxs) { Branch (1311:29): [True: 3.71M, False: 1.30M]
| 1312 | 3.71M | m_suboptimal_chunks.push_back(chunk_idx); | 1313 | | // Randomize the initial order of suboptimal chunks in the queue. | 1314 | 3.71M | SetIdx j = m_rng.randrange<SetIdx>(m_suboptimal_chunks.size()); | 1315 | 3.71M | if (j != m_suboptimal_chunks.size() - 1) { Branch (1315:17): [True: 1.85M, False: 1.85M]
| 1316 | 1.85M | std::swap(m_suboptimal_chunks.back(), m_suboptimal_chunks[j]); | 1317 | 1.85M | } | 1318 | 3.71M | } | 1319 | 1.30M | m_cost.StartOptimizingEnd(/*num_chunks=*/m_suboptimal_chunks.size()); | 1320 | 1.30M | } |
|
1321 | | |
1322 | | /** Try to improve the forest. Returns false if it is optimal, true otherwise. */ |
1323 | | bool OptimizeStep() noexcept |
1324 | 3.94M | { |
1325 | 3.94M | auto chunk_idx = PickChunkToOptimize(); |
1326 | 3.94M | if (chunk_idx == INVALID_SET_IDX) { Branch (1326:13): [True: 7, False: 14.6k]
Branch (1326:13): [True: 27, False: 13.9k]
Branch (1326:13): [True: 0, False: 3.91M]
|
1327 | | // No improvable chunk was found, we are done. |
1328 | 34 | return false; |
1329 | 34 | } |
1330 | 3.94M | auto [parent_idx, child_idx] = PickDependencyToSplit(chunk_idx); |
1331 | 3.94M | if (parent_idx == TxIdx(-1)) { Branch (1331:13): [True: 10.2k, False: 4.40k]
Branch (1331:13): [True: 13.7k, False: 204]
Branch (1331:13): [True: 3.79M, False: 123k]
|
1332 | | // Nothing to improve in chunk_idx. Need to continue with other chunks, if any. |
1333 | 3.81M | return !m_suboptimal_chunks.empty(); |
1334 | 3.81M | } |
1335 | | // Deactivate the found dependency and then make the state topological again with a |
1336 | | // sequence of merges. |
1337 | 128k | Improve(parent_idx, child_idx); |
1338 | 128k | return true; |
1339 | 3.94M | } _ZN17cluster_linearize19SpanningForestStateIN13bitset_detail9IntBitSetIjEENS_19SFLDefaultCostModelEE12OptimizeStepEv Line | Count | Source | 1324 | 14.6k | { | 1325 | 14.6k | auto chunk_idx = PickChunkToOptimize(); | 1326 | 14.6k | if (chunk_idx == INVALID_SET_IDX) { Branch (1326:13): [True: 7, False: 14.6k]
| 1327 | | // No improvable chunk was found, we are done. | 1328 | 7 | return false; | 1329 | 7 | } | 1330 | 14.6k | auto [parent_idx, child_idx] = PickDependencyToSplit(chunk_idx); | 1331 | 14.6k | if (parent_idx == TxIdx(-1)) { Branch (1331:13): [True: 10.2k, False: 4.40k]
| 1332 | | // Nothing to improve in chunk_idx. Need to continue with other chunks, if any. | 1333 | 10.2k | return !m_suboptimal_chunks.empty(); | 1334 | 10.2k | } | 1335 | | // Deactivate the found dependency and then make the state topological again with a | 1336 | | // sequence of merges. | 1337 | 4.40k | Improve(parent_idx, child_idx); | 1338 | 4.40k | return true; | 1339 | 14.6k | } |
_ZN17cluster_linearize19SpanningForestStateIN13bitset_detail14MultiIntBitSetImLj2EEENS_19SFLDefaultCostModelEE12OptimizeStepEv Line | Count | Source | 1324 | 14.0k | { | 1325 | 14.0k | auto chunk_idx = PickChunkToOptimize(); | 1326 | 14.0k | if (chunk_idx == INVALID_SET_IDX) { Branch (1326:13): [True: 27, False: 13.9k]
| 1327 | | // No improvable chunk was found, we are done. | 1328 | 27 | return false; | 1329 | 27 | } | 1330 | 13.9k | auto [parent_idx, child_idx] = PickDependencyToSplit(chunk_idx); | 1331 | 13.9k | if (parent_idx == TxIdx(-1)) { Branch (1331:13): [True: 13.7k, False: 204]
| 1332 | | // Nothing to improve in chunk_idx. Need to continue with other chunks, if any. | 1333 | 13.7k | return !m_suboptimal_chunks.empty(); | 1334 | 13.7k | } | 1335 | | // Deactivate the found dependency and then make the state topological again with a | 1336 | | // sequence of merges. | 1337 | 204 | Improve(parent_idx, child_idx); | 1338 | 204 | return true; | 1339 | 13.9k | } |
_ZN17cluster_linearize19SpanningForestStateIN13bitset_detail9IntBitSetImEENS_19SFLDefaultCostModelEE12OptimizeStepEv Line | Count | Source | 1324 | 3.91M | { | 1325 | 3.91M | auto chunk_idx = PickChunkToOptimize(); | 1326 | 3.91M | if (chunk_idx == INVALID_SET_IDX) { Branch (1326:13): [True: 0, False: 3.91M]
| 1327 | | // No improvable chunk was found, we are done. | 1328 | 0 | return false; | 1329 | 0 | } | 1330 | 3.91M | auto [parent_idx, child_idx] = PickDependencyToSplit(chunk_idx); | 1331 | 3.91M | if (parent_idx == TxIdx(-1)) { Branch (1331:13): [True: 3.79M, False: 123k]
| 1332 | | // Nothing to improve in chunk_idx. Need to continue with other chunks, if any. | 1333 | 3.79M | return !m_suboptimal_chunks.empty(); | 1334 | 3.79M | } | 1335 | | // Deactivate the found dependency and then make the state topological again with a | 1336 | | // sequence of merges. | 1337 | 123k | Improve(parent_idx, child_idx); | 1338 | 123k | return true; | 1339 | 3.91M | } |
|
1340 | | |
1341 | | /** Initialize data structure for minimizing the chunks. Can only be called if state is known |
1342 | | * to be optimal. OptimizeStep() cannot be called anymore afterwards. */ |
1343 | | void StartMinimizing() noexcept |
1344 | 1.30M | { |
1345 | 1.30M | m_cost.StartMinimizingBegin(); |
1346 | 1.30M | m_nonminimal_chunks.clear(); |
1347 | 1.30M | m_nonminimal_chunks.reserve(m_transaction_idxs.Count()); |
1348 | | // Gather all chunks, and for each, add it with a random pivot in it, and a random initial |
1349 | | // direction, to m_nonminimal_chunks. |
1350 | 3.80M | for (auto chunk_idx : m_chunk_idxs) { Branch (1350:29): [True: 9.19k, False: 1.17k]
Branch (1350:29): [True: 13.7k, False: 389]
Branch (1350:29): [True: 3.78M, False: 1.30M]
|
1351 | 3.80M | TxIdx pivot_idx = PickRandomTx(m_set_info[chunk_idx].transactions); |
1352 | 3.80M | m_nonminimal_chunks.emplace_back(chunk_idx, pivot_idx, m_rng.randbits<1>()); |
1353 | | // Randomize the initial order of nonminimal chunks in the queue. |
1354 | 3.80M | SetIdx j = m_rng.randrange<SetIdx>(m_nonminimal_chunks.size()); |
1355 | 3.80M | if (j != m_nonminimal_chunks.size() - 1) { Branch (1355:17): [True: 6.58k, False: 2.61k]
Branch (1355:17): [True: 12.4k, False: 1.33k]
Branch (1355:17): [True: 1.91M, False: 1.86M]
|
1356 | 1.93M | std::swap(m_nonminimal_chunks.back(), m_nonminimal_chunks[j]); |
1357 | 1.93M | } |
1358 | 3.80M | } |
1359 | 1.30M | m_cost.StartMinimizingEnd(/*num_chunks=*/m_nonminimal_chunks.size()); |
1360 | 1.30M | } _ZN17cluster_linearize19SpanningForestStateIN13bitset_detail9IntBitSetIjEENS_19SFLDefaultCostModelEE15StartMinimizingEv Line | Count | Source | 1344 | 1.17k | { | 1345 | 1.17k | m_cost.StartMinimizingBegin(); | 1346 | 1.17k | m_nonminimal_chunks.clear(); | 1347 | 1.17k | m_nonminimal_chunks.reserve(m_transaction_idxs.Count()); | 1348 | | // Gather all chunks, and for each, add it with a random pivot in it, and a random initial | 1349 | | // direction, to m_nonminimal_chunks. | 1350 | 9.19k | for (auto chunk_idx : m_chunk_idxs) { Branch (1350:29): [True: 9.19k, False: 1.17k]
| 1351 | 9.19k | TxIdx pivot_idx = PickRandomTx(m_set_info[chunk_idx].transactions); | 1352 | 9.19k | m_nonminimal_chunks.emplace_back(chunk_idx, pivot_idx, m_rng.randbits<1>()); | 1353 | | // Randomize the initial order of nonminimal chunks in the queue. | 1354 | 9.19k | SetIdx j = m_rng.randrange<SetIdx>(m_nonminimal_chunks.size()); | 1355 | 9.19k | if (j != m_nonminimal_chunks.size() - 1) { Branch (1355:17): [True: 6.58k, False: 2.61k]
| 1356 | 6.58k | std::swap(m_nonminimal_chunks.back(), m_nonminimal_chunks[j]); | 1357 | 6.58k | } | 1358 | 9.19k | } | 1359 | 1.17k | m_cost.StartMinimizingEnd(/*num_chunks=*/m_nonminimal_chunks.size()); | 1360 | 1.17k | } |
_ZN17cluster_linearize19SpanningForestStateIN13bitset_detail14MultiIntBitSetImLj2EEENS_19SFLDefaultCostModelEE15StartMinimizingEv Line | Count | Source | 1344 | 389 | { | 1345 | 389 | m_cost.StartMinimizingBegin(); | 1346 | 389 | m_nonminimal_chunks.clear(); | 1347 | 389 | m_nonminimal_chunks.reserve(m_transaction_idxs.Count()); | 1348 | | // Gather all chunks, and for each, add it with a random pivot in it, and a random initial | 1349 | | // direction, to m_nonminimal_chunks. | 1350 | 13.7k | for (auto chunk_idx : m_chunk_idxs) { Branch (1350:29): [True: 13.7k, False: 389]
| 1351 | 13.7k | TxIdx pivot_idx = PickRandomTx(m_set_info[chunk_idx].transactions); | 1352 | 13.7k | m_nonminimal_chunks.emplace_back(chunk_idx, pivot_idx, m_rng.randbits<1>()); | 1353 | | // Randomize the initial order of nonminimal chunks in the queue. | 1354 | 13.7k | SetIdx j = m_rng.randrange<SetIdx>(m_nonminimal_chunks.size()); | 1355 | 13.7k | if (j != m_nonminimal_chunks.size() - 1) { Branch (1355:17): [True: 12.4k, False: 1.33k]
| 1356 | 12.4k | std::swap(m_nonminimal_chunks.back(), m_nonminimal_chunks[j]); | 1357 | 12.4k | } | 1358 | 13.7k | } | 1359 | 389 | m_cost.StartMinimizingEnd(/*num_chunks=*/m_nonminimal_chunks.size()); | 1360 | 389 | } |
_ZN17cluster_linearize19SpanningForestStateIN13bitset_detail9IntBitSetImEENS_19SFLDefaultCostModelEE15StartMinimizingEv Line | Count | Source | 1344 | 1.30M | { | 1345 | 1.30M | m_cost.StartMinimizingBegin(); | 1346 | 1.30M | m_nonminimal_chunks.clear(); | 1347 | 1.30M | m_nonminimal_chunks.reserve(m_transaction_idxs.Count()); | 1348 | | // Gather all chunks, and for each, add it with a random pivot in it, and a random initial | 1349 | | // direction, to m_nonminimal_chunks. | 1350 | 3.78M | for (auto chunk_idx : m_chunk_idxs) { Branch (1350:29): [True: 3.78M, False: 1.30M]
| 1351 | 3.78M | TxIdx pivot_idx = PickRandomTx(m_set_info[chunk_idx].transactions); | 1352 | 3.78M | m_nonminimal_chunks.emplace_back(chunk_idx, pivot_idx, m_rng.randbits<1>()); | 1353 | | // Randomize the initial order of nonminimal chunks in the queue. | 1354 | 3.78M | SetIdx j = m_rng.randrange<SetIdx>(m_nonminimal_chunks.size()); | 1355 | 3.78M | if (j != m_nonminimal_chunks.size() - 1) { Branch (1355:17): [True: 1.91M, False: 1.86M]
| 1356 | 1.91M | std::swap(m_nonminimal_chunks.back(), m_nonminimal_chunks[j]); | 1357 | 1.91M | } | 1358 | 3.78M | } | 1359 | 1.30M | m_cost.StartMinimizingEnd(/*num_chunks=*/m_nonminimal_chunks.size()); | 1360 | 1.30M | } |
|
1361 | | |
1362 | | /** Try to reduce a chunk's size. Returns false if all chunks are minimal, true otherwise. */ |
1363 | | bool MinimizeStep() noexcept |
1364 | 6.66M | { |
1365 | | // If the queue of potentially-non-minimal chunks is empty, we are done. |
1366 | 6.66M | if (m_nonminimal_chunks.empty()) return false; Branch (1366:13): [True: 1.16k, False: 22.4k]
Branch (1366:13): [True: 389, False: 23.3k]
Branch (1366:13): [True: 1.29M, False: 5.31M]
|
1367 | 5.36M | m_cost.MinimizeStepBegin(); |
1368 | | // Pop an entry from the potentially-non-minimal chunk queue. |
1369 | 5.36M | auto [chunk_idx, pivot_idx, flags] = m_nonminimal_chunks.front(); |
1370 | 5.36M | m_nonminimal_chunks.pop_front(); |
1371 | 5.36M | auto& chunk_info = m_set_info[chunk_idx]; |
1372 | | /** Whether to move the pivot down rather than up. */ |
1373 | 5.36M | bool move_pivot_down = flags & 1; |
1374 | | /** Whether this is already the second stage. */ |
1375 | 5.36M | bool second_stage = flags & 2; |
1376 | | |
1377 | | // Find a random dependency whose top and bottom set feerates are equal, and which has |
1378 | | // pivot in bottom set (if move_pivot_down) or in top set (if !move_pivot_down). |
1379 | 5.36M | std::pair<TxIdx, TxIdx> candidate_dep; |
1380 | 5.36M | uint64_t candidate_tiebreak{0}; |
1381 | 5.36M | bool have_any = false; |
1382 | | // Iterate over all transactions. |
1383 | 10.4M | for (auto tx_idx : chunk_info.transactions) { Branch (1383:26): [True: 91.4k, False: 22.4k]
Branch (1383:26): [True: 47.6k, False: 23.3k]
Branch (1383:26): [True: 10.2M, False: 5.31M]
|
1384 | 10.4M | const auto& tx_data = m_tx_data[tx_idx]; |
1385 | | // Iterate over all active child dependencies of the transaction. |
1386 | 10.4M | for (auto child_idx : tx_data.active_children) { Branch (1386:33): [True: 69.0k, False: 91.4k]
Branch (1386:33): [True: 24.2k, False: 47.6k]
Branch (1386:33): [True: 4.96M, False: 10.2M]
|
1387 | 5.06M | const auto& dep_top_info = m_set_info[tx_data.dep_top_idx[child_idx]]; |
1388 | | // Skip if this dependency does not have equal top and bottom set feerates. Note |
1389 | | // that the top cannot have higher feerate than the bottom, or OptimizeSteps would |
1390 | | // have dealt with it. |
1391 | 5.06M | if (ByRatio{dep_top_info.feerate} < ByRatio{chunk_info.feerate}) continue; Branch (1391:21): [True: 15.5k, False: 53.4k]
Branch (1391:21): [True: 3.54k, False: 20.7k]
Branch (1391:21): [True: 1.92M, False: 3.04M]
|
1392 | 3.12M | have_any = true; |
1393 | | // Skip if this dependency does not have pivot in the right place. |
1394 | 3.12M | if (move_pivot_down == dep_top_info.transactions[pivot_idx]) continue; Branch (1394:21): [True: 27.4k, False: 25.9k]
Branch (1394:21): [True: 9.98k, False: 10.7k]
Branch (1394:21): [True: 1.10M, False: 1.93M]
|
1395 | | // Remember this as our chosen dependency if it has a better tiebreak. |
1396 | 1.97M | uint64_t tiebreak = m_rng.rand64() | 1; |
1397 | 1.97M | if (tiebreak > candidate_tiebreak) { Branch (1397:21): [True: 10.8k, False: 15.0k]
Branch (1397:21): [True: 6.42k, False: 4.31k]
Branch (1397:21): [True: 901k, False: 1.03M]
|
1398 | 918k | candidate_tiebreak = tiebreak; |
1399 | 918k | candidate_dep = {tx_idx, child_idx}; |
1400 | 918k | } |
1401 | 1.97M | } |
1402 | 10.4M | } |
1403 | 5.36M | m_cost.MinimizeStepMid(/*num_txns=*/chunk_info.transactions.Count()); |
1404 | | // If no dependencies have equal top and bottom set feerate, this chunk is minimal. |
1405 | 5.36M | if (!have_any) return true; Branch (1405:13): [True: 14.4k, False: 8.02k]
Branch (1405:13): [True: 17.8k, False: 5.53k]
Branch (1405:13): [True: 4.40M, False: 911k]
|
1406 | | // If all found dependencies have the pivot in the wrong place, try moving it in the other |
1407 | | // direction. If this was the second stage already, we are done. |
1408 | 925k | if (candidate_tiebreak == 0) { Branch (1408:13): [True: 1.64k, False: 6.37k]
Branch (1408:13): [True: 1.18k, False: 4.35k]
Branch (1408:13): [True: 249k, False: 662k]
|
1409 | | // Switch to other direction, and to second phase. |
1410 | 252k | flags ^= 3; |
1411 | 252k | if (!second_stage) m_nonminimal_chunks.emplace_back(chunk_idx, pivot_idx, flags); Branch (1411:17): [True: 1.60k, False: 40]
Branch (1411:17): [True: 1.17k, False: 8]
Branch (1411:17): [True: 248k, False: 1.27k]
|
1412 | 252k | return true; |
1413 | 252k | } |
1414 | | |
1415 | | // Otherwise, deactivate the dependency that was found. |
1416 | 672k | auto [parent_chunk_idx, child_chunk_idx] = Deactivate(candidate_dep.first, candidate_dep.second); |
1417 | | // Determine if there is a dependency from the new bottom to the new top (opposite from the |
1418 | | // dependency that was just deactivated). |
1419 | 672k | auto& parent_reachable = m_reachable[parent_chunk_idx].first; |
1420 | 672k | auto& child_chunk_txn = m_set_info[child_chunk_idx].transactions; |
1421 | 672k | if (parent_reachable.Overlaps(child_chunk_txn)) { Branch (1421:13): [True: 1.08k, False: 5.29k]
Branch (1421:13): [True: 314, False: 4.03k]
Branch (1421:13): [True: 27.0k, False: 635k]
|
1422 | | // A self-merge is needed. Note that the child_chunk_idx is the top, and |
1423 | | // parent_chunk_idx is the bottom, because we activate a dependency in the reverse |
1424 | | // direction compared to the deactivation above. |
1425 | 28.4k | auto merged_chunk_idx = MergeChunks(child_chunk_idx, parent_chunk_idx); |
1426 | | // Re-insert the chunk into the queue, in the same direction. Note that the chunk_idx |
1427 | | // will have changed. |
1428 | 28.4k | m_nonminimal_chunks.emplace_back(merged_chunk_idx, pivot_idx, flags); |
1429 | 28.4k | m_cost.MinimizeStepEnd(/*split=*/false); |
1430 | 644k | } else { |
1431 | | // No self-merge happens, and thus we have found a way to split the chunk. Create two |
1432 | | // smaller chunks, and add them to the queue. The one that contains the current pivot |
1433 | | // gets to continue with it in the same direction, to minimize the number of times we |
1434 | | // alternate direction. If we were in the second phase already, the newly created chunk |
1435 | | // inherits that too, because we know no split with the pivot on the other side is |
1436 | | // possible already. The new chunk without the current pivot gets a new randomly-chosen |
1437 | | // one. |
1438 | 644k | if (move_pivot_down) { Branch (1438:17): [True: 2.76k, False: 2.53k]
Branch (1438:17): [True: 2.02k, False: 2.01k]
Branch (1438:17): [True: 287k, False: 347k]
|
1439 | 292k | auto parent_pivot_idx = PickRandomTx(m_set_info[parent_chunk_idx].transactions); |
1440 | 292k | m_nonminimal_chunks.emplace_back(parent_chunk_idx, parent_pivot_idx, m_rng.randbits<1>()); |
1441 | 292k | m_nonminimal_chunks.emplace_back(child_chunk_idx, pivot_idx, flags); |
1442 | 351k | } else { |
1443 | 351k | auto child_pivot_idx = PickRandomTx(m_set_info[child_chunk_idx].transactions); |
1444 | 351k | m_nonminimal_chunks.emplace_back(parent_chunk_idx, pivot_idx, flags); |
1445 | 351k | m_nonminimal_chunks.emplace_back(child_chunk_idx, child_pivot_idx, m_rng.randbits<1>()); |
1446 | 351k | } |
1447 | 644k | if (m_rng.randbool()) { Branch (1447:17): [True: 2.61k, False: 2.67k]
Branch (1447:17): [True: 2.04k, False: 1.99k]
Branch (1447:17): [True: 316k, False: 318k]
|
1448 | 321k | std::swap(m_nonminimal_chunks.back(), m_nonminimal_chunks[m_nonminimal_chunks.size() - 2]); |
1449 | 321k | } |
1450 | 644k | m_cost.MinimizeStepEnd(/*split=*/true); |
1451 | 644k | } |
1452 | 672k | return true; |
1453 | 925k | } _ZN17cluster_linearize19SpanningForestStateIN13bitset_detail9IntBitSetIjEENS_19SFLDefaultCostModelEE12MinimizeStepEv Line | Count | Source | 1364 | 23.5k | { | 1365 | | // If the queue of potentially-non-minimal chunks is empty, we are done. | 1366 | 23.5k | if (m_nonminimal_chunks.empty()) return false; Branch (1366:13): [True: 1.16k, False: 22.4k]
| 1367 | 22.4k | m_cost.MinimizeStepBegin(); | 1368 | | // Pop an entry from the potentially-non-minimal chunk queue. | 1369 | 22.4k | auto [chunk_idx, pivot_idx, flags] = m_nonminimal_chunks.front(); | 1370 | 22.4k | m_nonminimal_chunks.pop_front(); | 1371 | 22.4k | auto& chunk_info = m_set_info[chunk_idx]; | 1372 | | /** Whether to move the pivot down rather than up. */ | 1373 | 22.4k | bool move_pivot_down = flags & 1; | 1374 | | /** Whether this is already the second stage. */ | 1375 | 22.4k | bool second_stage = flags & 2; | 1376 | | | 1377 | | // Find a random dependency whose top and bottom set feerates are equal, and which has | 1378 | | // pivot in bottom set (if move_pivot_down) or in top set (if !move_pivot_down). | 1379 | 22.4k | std::pair<TxIdx, TxIdx> candidate_dep; | 1380 | 22.4k | uint64_t candidate_tiebreak{0}; | 1381 | 22.4k | bool have_any = false; | 1382 | | // Iterate over all transactions. | 1383 | 91.4k | for (auto tx_idx : chunk_info.transactions) { Branch (1383:26): [True: 91.4k, False: 22.4k]
| 1384 | 91.4k | const auto& tx_data = m_tx_data[tx_idx]; | 1385 | | // Iterate over all active child dependencies of the transaction. | 1386 | 91.4k | for (auto child_idx : tx_data.active_children) { Branch (1386:33): [True: 69.0k, False: 91.4k]
| 1387 | 69.0k | const auto& dep_top_info = m_set_info[tx_data.dep_top_idx[child_idx]]; | 1388 | | // Skip if this dependency does not have equal top and bottom set feerates. Note | 1389 | | // that the top cannot have higher feerate than the bottom, or OptimizeSteps would | 1390 | | // have dealt with it. | 1391 | 69.0k | if (ByRatio{dep_top_info.feerate} < ByRatio{chunk_info.feerate}) continue; Branch (1391:21): [True: 15.5k, False: 53.4k]
| 1392 | 53.4k | have_any = true; | 1393 | | // Skip if this dependency does not have pivot in the right place. | 1394 | 53.4k | if (move_pivot_down == dep_top_info.transactions[pivot_idx]) continue; Branch (1394:21): [True: 27.4k, False: 25.9k]
| 1395 | | // Remember this as our chosen dependency if it has a better tiebreak. | 1396 | 25.9k | uint64_t tiebreak = m_rng.rand64() | 1; | 1397 | 25.9k | if (tiebreak > candidate_tiebreak) { Branch (1397:21): [True: 10.8k, False: 15.0k]
| 1398 | 10.8k | candidate_tiebreak = tiebreak; | 1399 | 10.8k | candidate_dep = {tx_idx, child_idx}; | 1400 | 10.8k | } | 1401 | 25.9k | } | 1402 | 91.4k | } | 1403 | 22.4k | m_cost.MinimizeStepMid(/*num_txns=*/chunk_info.transactions.Count()); | 1404 | | // If no dependencies have equal top and bottom set feerate, this chunk is minimal. | 1405 | 22.4k | if (!have_any) return true; Branch (1405:13): [True: 14.4k, False: 8.02k]
| 1406 | | // If all found dependencies have the pivot in the wrong place, try moving it in the other | 1407 | | // direction. If this was the second stage already, we are done. | 1408 | 8.02k | if (candidate_tiebreak == 0) { Branch (1408:13): [True: 1.64k, False: 6.37k]
| 1409 | | // Switch to other direction, and to second phase. | 1410 | 1.64k | flags ^= 3; | 1411 | 1.64k | if (!second_stage) m_nonminimal_chunks.emplace_back(chunk_idx, pivot_idx, flags); Branch (1411:17): [True: 1.60k, False: 40]
| 1412 | 1.64k | return true; | 1413 | 1.64k | } | 1414 | | | 1415 | | // Otherwise, deactivate the dependency that was found. | 1416 | 6.37k | auto [parent_chunk_idx, child_chunk_idx] = Deactivate(candidate_dep.first, candidate_dep.second); | 1417 | | // Determine if there is a dependency from the new bottom to the new top (opposite from the | 1418 | | // dependency that was just deactivated). | 1419 | 6.37k | auto& parent_reachable = m_reachable[parent_chunk_idx].first; | 1420 | 6.37k | auto& child_chunk_txn = m_set_info[child_chunk_idx].transactions; | 1421 | 6.37k | if (parent_reachable.Overlaps(child_chunk_txn)) { Branch (1421:13): [True: 1.08k, False: 5.29k]
| 1422 | | // A self-merge is needed. Note that the child_chunk_idx is the top, and | 1423 | | // parent_chunk_idx is the bottom, because we activate a dependency in the reverse | 1424 | | // direction compared to the deactivation above. | 1425 | 1.08k | auto merged_chunk_idx = MergeChunks(child_chunk_idx, parent_chunk_idx); | 1426 | | // Re-insert the chunk into the queue, in the same direction. Note that the chunk_idx | 1427 | | // will have changed. | 1428 | 1.08k | m_nonminimal_chunks.emplace_back(merged_chunk_idx, pivot_idx, flags); | 1429 | 1.08k | m_cost.MinimizeStepEnd(/*split=*/false); | 1430 | 5.29k | } else { | 1431 | | // No self-merge happens, and thus we have found a way to split the chunk. Create two | 1432 | | // smaller chunks, and add them to the queue. The one that contains the current pivot | 1433 | | // gets to continue with it in the same direction, to minimize the number of times we | 1434 | | // alternate direction. If we were in the second phase already, the newly created chunk | 1435 | | // inherits that too, because we know no split with the pivot on the other side is | 1436 | | // possible already. The new chunk without the current pivot gets a new randomly-chosen | 1437 | | // one. | 1438 | 5.29k | if (move_pivot_down) { Branch (1438:17): [True: 2.76k, False: 2.53k]
| 1439 | 2.76k | auto parent_pivot_idx = PickRandomTx(m_set_info[parent_chunk_idx].transactions); | 1440 | 2.76k | m_nonminimal_chunks.emplace_back(parent_chunk_idx, parent_pivot_idx, m_rng.randbits<1>()); | 1441 | 2.76k | m_nonminimal_chunks.emplace_back(child_chunk_idx, pivot_idx, flags); | 1442 | 2.76k | } else { | 1443 | 2.53k | auto child_pivot_idx = PickRandomTx(m_set_info[child_chunk_idx].transactions); | 1444 | 2.53k | m_nonminimal_chunks.emplace_back(parent_chunk_idx, pivot_idx, flags); | 1445 | 2.53k | m_nonminimal_chunks.emplace_back(child_chunk_idx, child_pivot_idx, m_rng.randbits<1>()); | 1446 | 2.53k | } | 1447 | 5.29k | if (m_rng.randbool()) { Branch (1447:17): [True: 2.61k, False: 2.67k]
| 1448 | 2.61k | std::swap(m_nonminimal_chunks.back(), m_nonminimal_chunks[m_nonminimal_chunks.size() - 2]); | 1449 | 2.61k | } | 1450 | 5.29k | m_cost.MinimizeStepEnd(/*split=*/true); | 1451 | 5.29k | } | 1452 | 6.37k | return true; | 1453 | 8.02k | } |
_ZN17cluster_linearize19SpanningForestStateIN13bitset_detail14MultiIntBitSetImLj2EEENS_19SFLDefaultCostModelEE12MinimizeStepEv Line | Count | Source | 1364 | 23.7k | { | 1365 | | // If the queue of potentially-non-minimal chunks is empty, we are done. | 1366 | 23.7k | if (m_nonminimal_chunks.empty()) return false; Branch (1366:13): [True: 389, False: 23.3k]
| 1367 | 23.3k | m_cost.MinimizeStepBegin(); | 1368 | | // Pop an entry from the potentially-non-minimal chunk queue. | 1369 | 23.3k | auto [chunk_idx, pivot_idx, flags] = m_nonminimal_chunks.front(); | 1370 | 23.3k | m_nonminimal_chunks.pop_front(); | 1371 | 23.3k | auto& chunk_info = m_set_info[chunk_idx]; | 1372 | | /** Whether to move the pivot down rather than up. */ | 1373 | 23.3k | bool move_pivot_down = flags & 1; | 1374 | | /** Whether this is already the second stage. */ | 1375 | 23.3k | bool second_stage = flags & 2; | 1376 | | | 1377 | | // Find a random dependency whose top and bottom set feerates are equal, and which has | 1378 | | // pivot in bottom set (if move_pivot_down) or in top set (if !move_pivot_down). | 1379 | 23.3k | std::pair<TxIdx, TxIdx> candidate_dep; | 1380 | 23.3k | uint64_t candidate_tiebreak{0}; | 1381 | 23.3k | bool have_any = false; | 1382 | | // Iterate over all transactions. | 1383 | 47.6k | for (auto tx_idx : chunk_info.transactions) { Branch (1383:26): [True: 47.6k, False: 23.3k]
| 1384 | 47.6k | const auto& tx_data = m_tx_data[tx_idx]; | 1385 | | // Iterate over all active child dependencies of the transaction. | 1386 | 47.6k | for (auto child_idx : tx_data.active_children) { Branch (1386:33): [True: 24.2k, False: 47.6k]
| 1387 | 24.2k | const auto& dep_top_info = m_set_info[tx_data.dep_top_idx[child_idx]]; | 1388 | | // Skip if this dependency does not have equal top and bottom set feerates. Note | 1389 | | // that the top cannot have higher feerate than the bottom, or OptimizeSteps would | 1390 | | // have dealt with it. | 1391 | 24.2k | if (ByRatio{dep_top_info.feerate} < ByRatio{chunk_info.feerate}) continue; Branch (1391:21): [True: 3.54k, False: 20.7k]
| 1392 | 20.7k | have_any = true; | 1393 | | // Skip if this dependency does not have pivot in the right place. | 1394 | 20.7k | if (move_pivot_down == dep_top_info.transactions[pivot_idx]) continue; Branch (1394:21): [True: 9.98k, False: 10.7k]
| 1395 | | // Remember this as our chosen dependency if it has a better tiebreak. | 1396 | 10.7k | uint64_t tiebreak = m_rng.rand64() | 1; | 1397 | 10.7k | if (tiebreak > candidate_tiebreak) { Branch (1397:21): [True: 6.42k, False: 4.31k]
| 1398 | 6.42k | candidate_tiebreak = tiebreak; | 1399 | 6.42k | candidate_dep = {tx_idx, child_idx}; | 1400 | 6.42k | } | 1401 | 10.7k | } | 1402 | 47.6k | } | 1403 | 23.3k | m_cost.MinimizeStepMid(/*num_txns=*/chunk_info.transactions.Count()); | 1404 | | // If no dependencies have equal top and bottom set feerate, this chunk is minimal. | 1405 | 23.3k | if (!have_any) return true; Branch (1405:13): [True: 17.8k, False: 5.53k]
| 1406 | | // If all found dependencies have the pivot in the wrong place, try moving it in the other | 1407 | | // direction. If this was the second stage already, we are done. | 1408 | 5.53k | if (candidate_tiebreak == 0) { Branch (1408:13): [True: 1.18k, False: 4.35k]
| 1409 | | // Switch to other direction, and to second phase. | 1410 | 1.18k | flags ^= 3; | 1411 | 1.18k | if (!second_stage) m_nonminimal_chunks.emplace_back(chunk_idx, pivot_idx, flags); Branch (1411:17): [True: 1.17k, False: 8]
| 1412 | 1.18k | return true; | 1413 | 1.18k | } | 1414 | | | 1415 | | // Otherwise, deactivate the dependency that was found. | 1416 | 4.35k | auto [parent_chunk_idx, child_chunk_idx] = Deactivate(candidate_dep.first, candidate_dep.second); | 1417 | | // Determine if there is a dependency from the new bottom to the new top (opposite from the | 1418 | | // dependency that was just deactivated). | 1419 | 4.35k | auto& parent_reachable = m_reachable[parent_chunk_idx].first; | 1420 | 4.35k | auto& child_chunk_txn = m_set_info[child_chunk_idx].transactions; | 1421 | 4.35k | if (parent_reachable.Overlaps(child_chunk_txn)) { Branch (1421:13): [True: 314, False: 4.03k]
| 1422 | | // A self-merge is needed. Note that the child_chunk_idx is the top, and | 1423 | | // parent_chunk_idx is the bottom, because we activate a dependency in the reverse | 1424 | | // direction compared to the deactivation above. | 1425 | 314 | auto merged_chunk_idx = MergeChunks(child_chunk_idx, parent_chunk_idx); | 1426 | | // Re-insert the chunk into the queue, in the same direction. Note that the chunk_idx | 1427 | | // will have changed. | 1428 | 314 | m_nonminimal_chunks.emplace_back(merged_chunk_idx, pivot_idx, flags); | 1429 | 314 | m_cost.MinimizeStepEnd(/*split=*/false); | 1430 | 4.03k | } else { | 1431 | | // No self-merge happens, and thus we have found a way to split the chunk. Create two | 1432 | | // smaller chunks, and add them to the queue. The one that contains the current pivot | 1433 | | // gets to continue with it in the same direction, to minimize the number of times we | 1434 | | // alternate direction. If we were in the second phase already, the newly created chunk | 1435 | | // inherits that too, because we know no split with the pivot on the other side is | 1436 | | // possible already. The new chunk without the current pivot gets a new randomly-chosen | 1437 | | // one. | 1438 | 4.03k | if (move_pivot_down) { Branch (1438:17): [True: 2.02k, False: 2.01k]
| 1439 | 2.02k | auto parent_pivot_idx = PickRandomTx(m_set_info[parent_chunk_idx].transactions); | 1440 | 2.02k | m_nonminimal_chunks.emplace_back(parent_chunk_idx, parent_pivot_idx, m_rng.randbits<1>()); | 1441 | 2.02k | m_nonminimal_chunks.emplace_back(child_chunk_idx, pivot_idx, flags); | 1442 | 2.02k | } else { | 1443 | 2.01k | auto child_pivot_idx = PickRandomTx(m_set_info[child_chunk_idx].transactions); | 1444 | 2.01k | m_nonminimal_chunks.emplace_back(parent_chunk_idx, pivot_idx, flags); | 1445 | 2.01k | m_nonminimal_chunks.emplace_back(child_chunk_idx, child_pivot_idx, m_rng.randbits<1>()); | 1446 | 2.01k | } | 1447 | 4.03k | if (m_rng.randbool()) { Branch (1447:17): [True: 2.04k, False: 1.99k]
| 1448 | 2.04k | std::swap(m_nonminimal_chunks.back(), m_nonminimal_chunks[m_nonminimal_chunks.size() - 2]); | 1449 | 2.04k | } | 1450 | 4.03k | m_cost.MinimizeStepEnd(/*split=*/true); | 1451 | 4.03k | } | 1452 | 4.35k | return true; | 1453 | 5.53k | } |
_ZN17cluster_linearize19SpanningForestStateIN13bitset_detail9IntBitSetImEENS_19SFLDefaultCostModelEE12MinimizeStepEv Line | Count | Source | 1364 | 6.61M | { | 1365 | | // If the queue of potentially-non-minimal chunks is empty, we are done. | 1366 | 6.61M | if (m_nonminimal_chunks.empty()) return false; Branch (1366:13): [True: 1.29M, False: 5.31M]
| 1367 | 5.31M | m_cost.MinimizeStepBegin(); | 1368 | | // Pop an entry from the potentially-non-minimal chunk queue. | 1369 | 5.31M | auto [chunk_idx, pivot_idx, flags] = m_nonminimal_chunks.front(); | 1370 | 5.31M | m_nonminimal_chunks.pop_front(); | 1371 | 5.31M | auto& chunk_info = m_set_info[chunk_idx]; | 1372 | | /** Whether to move the pivot down rather than up. */ | 1373 | 5.31M | bool move_pivot_down = flags & 1; | 1374 | | /** Whether this is already the second stage. */ | 1375 | 5.31M | bool second_stage = flags & 2; | 1376 | | | 1377 | | // Find a random dependency whose top and bottom set feerates are equal, and which has | 1378 | | // pivot in bottom set (if move_pivot_down) or in top set (if !move_pivot_down). | 1379 | 5.31M | std::pair<TxIdx, TxIdx> candidate_dep; | 1380 | 5.31M | uint64_t candidate_tiebreak{0}; | 1381 | 5.31M | bool have_any = false; | 1382 | | // Iterate over all transactions. | 1383 | 10.2M | for (auto tx_idx : chunk_info.transactions) { Branch (1383:26): [True: 10.2M, False: 5.31M]
| 1384 | 10.2M | const auto& tx_data = m_tx_data[tx_idx]; | 1385 | | // Iterate over all active child dependencies of the transaction. | 1386 | 10.2M | for (auto child_idx : tx_data.active_children) { Branch (1386:33): [True: 4.96M, False: 10.2M]
| 1387 | 4.96M | const auto& dep_top_info = m_set_info[tx_data.dep_top_idx[child_idx]]; | 1388 | | // Skip if this dependency does not have equal top and bottom set feerates. Note | 1389 | | // that the top cannot have higher feerate than the bottom, or OptimizeSteps would | 1390 | | // have dealt with it. | 1391 | 4.96M | if (ByRatio{dep_top_info.feerate} < ByRatio{chunk_info.feerate}) continue; Branch (1391:21): [True: 1.92M, False: 3.04M]
| 1392 | 3.04M | have_any = true; | 1393 | | // Skip if this dependency does not have pivot in the right place. | 1394 | 3.04M | if (move_pivot_down == dep_top_info.transactions[pivot_idx]) continue; Branch (1394:21): [True: 1.10M, False: 1.93M]
| 1395 | | // Remember this as our chosen dependency if it has a better tiebreak. | 1396 | 1.93M | uint64_t tiebreak = m_rng.rand64() | 1; | 1397 | 1.93M | if (tiebreak > candidate_tiebreak) { Branch (1397:21): [True: 901k, False: 1.03M]
| 1398 | 901k | candidate_tiebreak = tiebreak; | 1399 | 901k | candidate_dep = {tx_idx, child_idx}; | 1400 | 901k | } | 1401 | 1.93M | } | 1402 | 10.2M | } | 1403 | 5.31M | m_cost.MinimizeStepMid(/*num_txns=*/chunk_info.transactions.Count()); | 1404 | | // If no dependencies have equal top and bottom set feerate, this chunk is minimal. | 1405 | 5.31M | if (!have_any) return true; Branch (1405:13): [True: 4.40M, False: 911k]
| 1406 | | // If all found dependencies have the pivot in the wrong place, try moving it in the other | 1407 | | // direction. If this was the second stage already, we are done. | 1408 | 911k | if (candidate_tiebreak == 0) { Branch (1408:13): [True: 249k, False: 662k]
| 1409 | | // Switch to other direction, and to second phase. | 1410 | 249k | flags ^= 3; | 1411 | 249k | if (!second_stage) m_nonminimal_chunks.emplace_back(chunk_idx, pivot_idx, flags); Branch (1411:17): [True: 248k, False: 1.27k]
| 1412 | 249k | return true; | 1413 | 249k | } | 1414 | | | 1415 | | // Otherwise, deactivate the dependency that was found. | 1416 | 662k | auto [parent_chunk_idx, child_chunk_idx] = Deactivate(candidate_dep.first, candidate_dep.second); | 1417 | | // Determine if there is a dependency from the new bottom to the new top (opposite from the | 1418 | | // dependency that was just deactivated). | 1419 | 662k | auto& parent_reachable = m_reachable[parent_chunk_idx].first; | 1420 | 662k | auto& child_chunk_txn = m_set_info[child_chunk_idx].transactions; | 1421 | 662k | if (parent_reachable.Overlaps(child_chunk_txn)) { Branch (1421:13): [True: 27.0k, False: 635k]
| 1422 | | // A self-merge is needed. Note that the child_chunk_idx is the top, and | 1423 | | // parent_chunk_idx is the bottom, because we activate a dependency in the reverse | 1424 | | // direction compared to the deactivation above. | 1425 | 27.0k | auto merged_chunk_idx = MergeChunks(child_chunk_idx, parent_chunk_idx); | 1426 | | // Re-insert the chunk into the queue, in the same direction. Note that the chunk_idx | 1427 | | // will have changed. | 1428 | 27.0k | m_nonminimal_chunks.emplace_back(merged_chunk_idx, pivot_idx, flags); | 1429 | 27.0k | m_cost.MinimizeStepEnd(/*split=*/false); | 1430 | 635k | } else { | 1431 | | // No self-merge happens, and thus we have found a way to split the chunk. Create two | 1432 | | // smaller chunks, and add them to the queue. The one that contains the current pivot | 1433 | | // gets to continue with it in the same direction, to minimize the number of times we | 1434 | | // alternate direction. If we were in the second phase already, the newly created chunk | 1435 | | // inherits that too, because we know no split with the pivot on the other side is | 1436 | | // possible already. The new chunk without the current pivot gets a new randomly-chosen | 1437 | | // one. | 1438 | 635k | if (move_pivot_down) { Branch (1438:17): [True: 287k, False: 347k]
| 1439 | 287k | auto parent_pivot_idx = PickRandomTx(m_set_info[parent_chunk_idx].transactions); | 1440 | 287k | m_nonminimal_chunks.emplace_back(parent_chunk_idx, parent_pivot_idx, m_rng.randbits<1>()); | 1441 | 287k | m_nonminimal_chunks.emplace_back(child_chunk_idx, pivot_idx, flags); | 1442 | 347k | } else { | 1443 | 347k | auto child_pivot_idx = PickRandomTx(m_set_info[child_chunk_idx].transactions); | 1444 | 347k | m_nonminimal_chunks.emplace_back(parent_chunk_idx, pivot_idx, flags); | 1445 | 347k | m_nonminimal_chunks.emplace_back(child_chunk_idx, child_pivot_idx, m_rng.randbits<1>()); | 1446 | 347k | } | 1447 | 635k | if (m_rng.randbool()) { Branch (1447:17): [True: 316k, False: 318k]
| 1448 | 316k | std::swap(m_nonminimal_chunks.back(), m_nonminimal_chunks[m_nonminimal_chunks.size() - 2]); | 1449 | 316k | } | 1450 | 635k | m_cost.MinimizeStepEnd(/*split=*/true); | 1451 | 635k | } | 1452 | 662k | return true; | 1453 | 911k | } |
|
1454 | | |
1455 | | /** Construct a topologically-valid linearization from the current forest state. Must be |
1456 | | * topological. fallback_order is a comparator that defines a strong order for DepGraphIndexes |
1457 | | * in this cluster, used to order equal-feerate transactions and chunks. |
1458 | | * |
1459 | | * Specifically, the resulting order consists of: |
1460 | | * - The chunks of the current SFL state, sorted by (in decreasing order of priority): |
1461 | | * - topology (parents before children) |
1462 | | * - highest chunk feerate first |
1463 | | * - smallest chunk size first |
1464 | | * - the chunk with the lowest maximum transaction, by fallback_order, first |
1465 | | * - The transactions within a chunk, sorted by (in decreasing order of priority): |
1466 | | * - topology (parents before children) |
1467 | | * - highest tx feerate first |
1468 | | * - smallest tx size first |
1469 | | * - the lowest transaction, by fallback_order, first |
1470 | | */ |
1471 | | std::vector<DepGraphIndex> GetLinearization(const StrongComparator<DepGraphIndex> auto& fallback_order) noexcept |
1472 | 1.32M | { |
1473 | 1.32M | m_cost.GetLinearizationBegin(); |
1474 | | /** The output linearization. */ |
1475 | 1.32M | std::vector<DepGraphIndex> ret; |
1476 | 1.32M | ret.reserve(m_set_info.size()); |
1477 | | /** A heap with all chunks (by set index) that can currently be included, sorted by |
1478 | | * chunk feerate (high to low), chunk size (small to large), and by least maximum element |
1479 | | * according to the fallback order (which is the second pair element). */ |
1480 | 1.32M | std::array<std::pair<SetIdx, TxIdx>, SetType::Size()> ready_chunks; |
1481 | | /** The number of entries of ready_chunks in use. */ |
1482 | 1.32M | unsigned num_ready_chunks{0}; |
1483 | | /** For every chunk, indexed by SetIdx, the number of unmet dependencies the chunk has on |
1484 | | * other chunks (not including dependencies within the chunk itself). */ |
1485 | 1.32M | std::array<TxIdx, SetType::Size()> chunk_deps; |
1486 | 1.32M | std::fill_n(chunk_deps.begin(), m_set_info.size(), TxIdx{0}); |
1487 | | /** For every transaction, indexed by TxIdx, the number of unmet dependencies the |
1488 | | * transaction has. */ |
1489 | 1.32M | std::array<TxIdx, SetType::Size()> tx_deps; |
1490 | 1.32M | std::fill_n(tx_deps.begin(), m_tx_data.size(), TxIdx{0}); |
1491 | | /** A heap with all transactions within the current chunk that can be included, sorted by |
1492 | | * tx feerate (high to low), tx size (small to large), and fallback order. */ |
1493 | 1.32M | std::array<TxIdx, SetType::Size()> ready_tx; |
1494 | | /** The number of entries of ready_tx in use. */ |
1495 | 1.32M | unsigned num_ready_tx{0}; |
1496 | | // Populate chunk_deps and tx_deps. |
1497 | 1.32M | unsigned num_deps{0}; |
1498 | 6.75M | for (TxIdx chl_idx : m_transaction_idxs) { Branch (1498:28): [True: 67.1k, False: 2.63k]
Branch (1498:28): [True: 21.1k, False: 389]
Branch (1498:28): [True: 6.66M, False: 1.32M]
|
1499 | 6.75M | const auto& chl_data = m_tx_data[chl_idx]; |
1500 | 6.75M | tx_deps[chl_idx] = chl_data.parents.Count(); |
1501 | 6.75M | num_deps += tx_deps[chl_idx]; |
1502 | 6.75M | auto chl_chunk_idx = chl_data.chunk_idx; |
1503 | 6.75M | auto& chl_chunk_info = m_set_info[chl_chunk_idx]; |
1504 | 6.75M | chunk_deps[chl_chunk_idx] += (chl_data.parents - chl_chunk_info.transactions).Count(); |
1505 | 6.75M | } |
1506 | | /** Function to compute the highest element of a chunk, by fallback_order. */ |
1507 | 4.57M | auto max_fallback_fn = [&](SetIdx chunk_idx) noexcept { |
1508 | 4.57M | auto& chunk = m_set_info[chunk_idx].transactions; |
1509 | 4.57M | auto it = chunk.begin(); |
1510 | 4.57M | DepGraphIndex ret = *it; |
1511 | 4.57M | ++it; |
1512 | 6.75M | while (it != chunk.end()) { Branch (1512:20): [True: 31.3k, False: 35.7k]
Branch (1512:20): [True: 3.33k, False: 17.8k]
Branch (1512:20): [True: 2.14M, False: 4.52M]
|
1513 | 2.17M | if (fallback_order(*it, ret) > 0) ret = *it; Branch (1513:21): [True: 31.3k, False: 0]
Branch (1513:21): [True: 863, False: 2.47k]
Branch (1513:21): [True: 668k, False: 1.47M]
|
1514 | 2.17M | ++it; |
1515 | 2.17M | } |
1516 | 4.57M | return ret; |
1517 | 4.57M | }; _ZZN17cluster_linearize19SpanningForestStateIN13bitset_detail9IntBitSetIjEENS_19SFLDefaultCostModelEE16GetLinearizationITkNS_16StrongComparatorIjEESt17compare_three_wayEESt6vectorIjSaIjEERKT_ENKUlhE_clEh Line | Count | Source | 1507 | 35.7k | auto max_fallback_fn = [&](SetIdx chunk_idx) noexcept { | 1508 | 35.7k | auto& chunk = m_set_info[chunk_idx].transactions; | 1509 | 35.7k | auto it = chunk.begin(); | 1510 | 35.7k | DepGraphIndex ret = *it; | 1511 | 35.7k | ++it; | 1512 | 67.1k | while (it != chunk.end()) { Branch (1512:20): [True: 31.3k, False: 35.7k]
| 1513 | 31.3k | if (fallback_order(*it, ret) > 0) ret = *it; Branch (1513:21): [True: 31.3k, False: 0]
| 1514 | 31.3k | ++it; | 1515 | 31.3k | } | 1516 | 35.7k | return ret; | 1517 | 35.7k | }; |
txgraph.cpp:_ZZN17cluster_linearize19SpanningForestStateIN13bitset_detail14MultiIntBitSetImLj2EEENS_19SFLDefaultCostModelEE16GetLinearizationITkNS_16StrongComparatorIjEEZ19txgraph_fuzz_targetSt4spanIKhLm18446744073709551615EEE3$_4EESt6vectorIjSaIjEERKT_ENKUlhE_clEh Line | Count | Source | 1507 | 17.8k | auto max_fallback_fn = [&](SetIdx chunk_idx) noexcept { | 1508 | 17.8k | auto& chunk = m_set_info[chunk_idx].transactions; | 1509 | 17.8k | auto it = chunk.begin(); | 1510 | 17.8k | DepGraphIndex ret = *it; | 1511 | 17.8k | ++it; | 1512 | 21.1k | while (it != chunk.end()) { Branch (1512:20): [True: 3.33k, False: 17.8k]
| 1513 | 3.33k | if (fallback_order(*it, ret) > 0) ret = *it; Branch (1513:21): [True: 863, False: 2.47k]
| 1514 | 3.33k | ++it; | 1515 | 3.33k | } | 1516 | 17.8k | return ret; | 1517 | 17.8k | }; |
txgraph.cpp:_ZZN17cluster_linearize19SpanningForestStateIN13bitset_detail9IntBitSetImEENS_19SFLDefaultCostModelEE16GetLinearizationITkNS_16StrongComparatorIjEEZN12_GLOBAL__N_118GenericClusterImpl11RelinearizeERNS8_11TxGraphImplEimE3$_0EESt6vectorIjSaIjEERKT_ENKUlhE_clEh Line | Count | Source | 1507 | 4.52M | auto max_fallback_fn = [&](SetIdx chunk_idx) noexcept { | 1508 | 4.52M | auto& chunk = m_set_info[chunk_idx].transactions; | 1509 | 4.52M | auto it = chunk.begin(); | 1510 | 4.52M | DepGraphIndex ret = *it; | 1511 | 4.52M | ++it; | 1512 | 6.66M | while (it != chunk.end()) { Branch (1512:20): [True: 2.14M, False: 4.52M]
| 1513 | 2.14M | if (fallback_order(*it, ret) > 0) ret = *it; Branch (1513:21): [True: 668k, False: 1.47M]
| 1514 | 2.14M | ++it; | 1515 | 2.14M | } | 1516 | 4.52M | return ret; | 1517 | 4.52M | }; |
|
1518 | | /** Comparison function for the transaction heap. Note that it is a max-heap, so |
1519 | | * tx_cmp_fn(a, b) == true means "a appears after b in the linearization". */ |
1520 | 2.55M | auto tx_cmp_fn = [&](const auto& a, const auto& b) noexcept { |
1521 | | // Bail out for identical transactions. |
1522 | 2.55M | if (a == b) return false; Branch (1522:17): [True: 0, False: 60.5k]
Branch (1522:17): [True: 0, False: 4.51k]
Branch (1522:17): [True: 0, False: 2.49M]
|
1523 | | // First sort by increasing transaction feerate. |
1524 | 2.55M | auto& a_feerate = m_depgraph.FeeRate(a); |
1525 | 2.55M | auto& b_feerate = m_depgraph.FeeRate(b); |
1526 | 2.55M | auto feerate_cmp = ByRatio{a_feerate} <=> ByRatio{b_feerate}; |
1527 | 2.55M | if (feerate_cmp != 0) return feerate_cmp < 0; Branch (1527:17): [True: 23.4k, False: 37.1k]
Branch (1527:17): [True: 1.41k, False: 3.09k]
Branch (1527:17): [True: 1.12M, False: 1.36M]
|
1528 | | // Then by decreasing transaction size. |
1529 | 1.40M | if (a_feerate.size != b_feerate.size) { Branch (1529:17): [True: 7.41k, False: 29.7k]
Branch (1529:17): [True: 940, False: 2.15k]
Branch (1529:17): [True: 100k, False: 1.26M]
|
1530 | 108k | return a_feerate.size > b_feerate.size; |
1531 | 108k | } |
1532 | | // Tie-break by decreasing fallback_order. |
1533 | 1.29M | auto fallback_cmp = fallback_order(a, b); |
1534 | 1.29M | if (fallback_cmp != 0) return fallback_cmp > 0; Branch (1534:17): [True: 29.7k, False: 0]
Branch (1534:17): [True: 2.15k, False: 0]
Branch (1534:17): [True: 1.26M, False: 0]
|
1535 | | // This should not be hit, because fallback_order defines a strong ordering. |
1536 | 0 | Assume(false); |
1537 | 0 | return a < b; |
1538 | 1.29M | }; _ZZN17cluster_linearize19SpanningForestStateIN13bitset_detail9IntBitSetIjEENS_19SFLDefaultCostModelEE16GetLinearizationITkNS_16StrongComparatorIjEESt17compare_three_wayEESt6vectorIjSaIjEERKT_ENKUlSE_RKT0_E_clIjjEEDaSE_SH_ Line | Count | Source | 1520 | 60.5k | auto tx_cmp_fn = [&](const auto& a, const auto& b) noexcept { | 1521 | | // Bail out for identical transactions. | 1522 | 60.5k | if (a == b) return false; Branch (1522:17): [True: 0, False: 60.5k]
| 1523 | | // First sort by increasing transaction feerate. | 1524 | 60.5k | auto& a_feerate = m_depgraph.FeeRate(a); | 1525 | 60.5k | auto& b_feerate = m_depgraph.FeeRate(b); | 1526 | 60.5k | auto feerate_cmp = ByRatio{a_feerate} <=> ByRatio{b_feerate}; | 1527 | 60.5k | if (feerate_cmp != 0) return feerate_cmp < 0; Branch (1527:17): [True: 23.4k, False: 37.1k]
| 1528 | | // Then by decreasing transaction size. | 1529 | 37.1k | if (a_feerate.size != b_feerate.size) { Branch (1529:17): [True: 7.41k, False: 29.7k]
| 1530 | 7.41k | return a_feerate.size > b_feerate.size; | 1531 | 7.41k | } | 1532 | | // Tie-break by decreasing fallback_order. | 1533 | 29.7k | auto fallback_cmp = fallback_order(a, b); | 1534 | 29.7k | if (fallback_cmp != 0) return fallback_cmp > 0; Branch (1534:17): [True: 29.7k, False: 0]
| 1535 | | // This should not be hit, because fallback_order defines a strong ordering. | 1536 | 0 | Assume(false); | 1537 | 0 | return a < b; | 1538 | 29.7k | }; |
txgraph.cpp:_ZZN17cluster_linearize19SpanningForestStateIN13bitset_detail14MultiIntBitSetImLj2EEENS_19SFLDefaultCostModelEE16GetLinearizationITkNS_16StrongComparatorIjEEZ19txgraph_fuzz_targetSt4spanIKhLm18446744073709551615EEE3$_4EESt6vectorIjSaIjEERKT_ENKUlSH_RKT0_E_clIjjEEDaSH_SK_ Line | Count | Source | 1520 | 4.51k | auto tx_cmp_fn = [&](const auto& a, const auto& b) noexcept { | 1521 | | // Bail out for identical transactions. | 1522 | 4.51k | if (a == b) return false; Branch (1522:17): [True: 0, False: 4.51k]
| 1523 | | // First sort by increasing transaction feerate. | 1524 | 4.51k | auto& a_feerate = m_depgraph.FeeRate(a); | 1525 | 4.51k | auto& b_feerate = m_depgraph.FeeRate(b); | 1526 | 4.51k | auto feerate_cmp = ByRatio{a_feerate} <=> ByRatio{b_feerate}; | 1527 | 4.51k | if (feerate_cmp != 0) return feerate_cmp < 0; Branch (1527:17): [True: 1.41k, False: 3.09k]
| 1528 | | // Then by decreasing transaction size. | 1529 | 3.09k | if (a_feerate.size != b_feerate.size) { Branch (1529:17): [True: 940, False: 2.15k]
| 1530 | 940 | return a_feerate.size > b_feerate.size; | 1531 | 940 | } | 1532 | | // Tie-break by decreasing fallback_order. | 1533 | 2.15k | auto fallback_cmp = fallback_order(a, b); | 1534 | 2.15k | if (fallback_cmp != 0) return fallback_cmp > 0; Branch (1534:17): [True: 2.15k, False: 0]
| 1535 | | // This should not be hit, because fallback_order defines a strong ordering. | 1536 | 0 | Assume(false); | 1537 | 0 | return a < b; | 1538 | 2.15k | }; |
txgraph.cpp:_ZZN17cluster_linearize19SpanningForestStateIN13bitset_detail9IntBitSetImEENS_19SFLDefaultCostModelEE16GetLinearizationITkNS_16StrongComparatorIjEEZN12_GLOBAL__N_118GenericClusterImpl11RelinearizeERNS8_11TxGraphImplEimE3$_0EESt6vectorIjSaIjEERKT_ENKUlSI_RKT0_E_clIjjEEDaSI_SL_ Line | Count | Source | 1520 | 2.49M | auto tx_cmp_fn = [&](const auto& a, const auto& b) noexcept { | 1521 | | // Bail out for identical transactions. | 1522 | 2.49M | if (a == b) return false; Branch (1522:17): [True: 0, False: 2.49M]
| 1523 | | // First sort by increasing transaction feerate. | 1524 | 2.49M | auto& a_feerate = m_depgraph.FeeRate(a); | 1525 | 2.49M | auto& b_feerate = m_depgraph.FeeRate(b); | 1526 | 2.49M | auto feerate_cmp = ByRatio{a_feerate} <=> ByRatio{b_feerate}; | 1527 | 2.49M | if (feerate_cmp != 0) return feerate_cmp < 0; Branch (1527:17): [True: 1.12M, False: 1.36M]
| 1528 | | // Then by decreasing transaction size. | 1529 | 1.36M | if (a_feerate.size != b_feerate.size) { Branch (1529:17): [True: 100k, False: 1.26M]
| 1530 | 100k | return a_feerate.size > b_feerate.size; | 1531 | 100k | } | 1532 | | // Tie-break by decreasing fallback_order. | 1533 | 1.26M | auto fallback_cmp = fallback_order(a, b); | 1534 | 1.26M | if (fallback_cmp != 0) return fallback_cmp > 0; Branch (1534:17): [True: 1.26M, False: 0]
| 1535 | | // This should not be hit, because fallback_order defines a strong ordering. | 1536 | 0 | Assume(false); | 1537 | 0 | return a < b; | 1538 | 1.26M | }; |
|
1539 | | // Construct a heap with all chunks that have no out-of-chunk dependencies. |
1540 | | /** Comparison function for the chunk heap. Note that it is a max-heap, so |
1541 | | * chunk_cmp_fn(a, b) == true means "a appears after b in the linearization". */ |
1542 | 8.83M | auto chunk_cmp_fn = [&](const auto& a, const auto& b) noexcept { |
1543 | | // Bail out for identical chunks. |
1544 | 8.83M | if (a.first == b.first) return false; Branch (1544:17): [True: 0, False: 119k]
Branch (1544:17): [True: 0, False: 109k]
Branch (1544:17): [True: 0, False: 8.60M]
|
1545 | | // First sort by increasing chunk feerate. |
1546 | 8.83M | auto& chunk_feerate_a = m_set_info[a.first].feerate; |
1547 | 8.83M | auto& chunk_feerate_b = m_set_info[b.first].feerate; |
1548 | 8.83M | auto feerate_cmp = ByRatio{chunk_feerate_a} <=> ByRatio{chunk_feerate_b}; |
1549 | 8.83M | if (feerate_cmp != 0) return feerate_cmp < 0; Branch (1549:17): [True: 53.3k, False: 66.1k]
Branch (1549:17): [True: 36.0k, False: 73.9k]
Branch (1549:17): [True: 4.09M, False: 4.50M]
|
1550 | | // Then by decreasing chunk size. |
1551 | 4.64M | if (chunk_feerate_a.size != chunk_feerate_b.size) { Branch (1551:17): [True: 16.3k, False: 49.7k]
Branch (1551:17): [True: 16.1k, False: 57.8k]
Branch (1551:17): [True: 307k, False: 4.19M]
|
1552 | 340k | return chunk_feerate_a.size > chunk_feerate_b.size; |
1553 | 340k | } |
1554 | | // Tie-break by decreasing fallback_order. |
1555 | 4.30M | auto fallback_cmp = fallback_order(a.second, b.second); |
1556 | 4.30M | if (fallback_cmp != 0) return fallback_cmp > 0; Branch (1556:17): [True: 49.7k, False: 0]
Branch (1556:17): [True: 57.8k, False: 0]
Branch (1556:17): [True: 4.19M, False: 0]
|
1557 | | // This should not be hit, because fallback_order defines a strong ordering. |
1558 | 0 | Assume(false); |
1559 | 0 | return a.second < b.second; |
1560 | 4.30M | }; _ZZN17cluster_linearize19SpanningForestStateIN13bitset_detail9IntBitSetIjEENS_19SFLDefaultCostModelEE16GetLinearizationITkNS_16StrongComparatorIjEESt17compare_three_wayEESt6vectorIjSaIjEERKT_ENKUlSE_RKT0_E0_clISt4pairIhjESL_EEDaSE_SH_ Line | Count | Source | 1542 | 119k | auto chunk_cmp_fn = [&](const auto& a, const auto& b) noexcept { | 1543 | | // Bail out for identical chunks. | 1544 | 119k | if (a.first == b.first) return false; Branch (1544:17): [True: 0, False: 119k]
| 1545 | | // First sort by increasing chunk feerate. | 1546 | 119k | auto& chunk_feerate_a = m_set_info[a.first].feerate; | 1547 | 119k | auto& chunk_feerate_b = m_set_info[b.first].feerate; | 1548 | 119k | auto feerate_cmp = ByRatio{chunk_feerate_a} <=> ByRatio{chunk_feerate_b}; | 1549 | 119k | if (feerate_cmp != 0) return feerate_cmp < 0; Branch (1549:17): [True: 53.3k, False: 66.1k]
| 1550 | | // Then by decreasing chunk size. | 1551 | 66.1k | if (chunk_feerate_a.size != chunk_feerate_b.size) { Branch (1551:17): [True: 16.3k, False: 49.7k]
| 1552 | 16.3k | return chunk_feerate_a.size > chunk_feerate_b.size; | 1553 | 16.3k | } | 1554 | | // Tie-break by decreasing fallback_order. | 1555 | 49.7k | auto fallback_cmp = fallback_order(a.second, b.second); | 1556 | 49.7k | if (fallback_cmp != 0) return fallback_cmp > 0; Branch (1556:17): [True: 49.7k, False: 0]
| 1557 | | // This should not be hit, because fallback_order defines a strong ordering. | 1558 | 0 | Assume(false); | 1559 | 0 | return a.second < b.second; | 1560 | 49.7k | }; |
txgraph.cpp:_ZZN17cluster_linearize19SpanningForestStateIN13bitset_detail14MultiIntBitSetImLj2EEENS_19SFLDefaultCostModelEE16GetLinearizationITkNS_16StrongComparatorIjEEZ19txgraph_fuzz_targetSt4spanIKhLm18446744073709551615EEE3$_4EESt6vectorIjSaIjEERKT_ENKUlSH_RKT0_E0_clISt4pairIhjESO_EEDaSH_SK_ Line | Count | Source | 1542 | 109k | auto chunk_cmp_fn = [&](const auto& a, const auto& b) noexcept { | 1543 | | // Bail out for identical chunks. | 1544 | 109k | if (a.first == b.first) return false; Branch (1544:17): [True: 0, False: 109k]
| 1545 | | // First sort by increasing chunk feerate. | 1546 | 109k | auto& chunk_feerate_a = m_set_info[a.first].feerate; | 1547 | 109k | auto& chunk_feerate_b = m_set_info[b.first].feerate; | 1548 | 109k | auto feerate_cmp = ByRatio{chunk_feerate_a} <=> ByRatio{chunk_feerate_b}; | 1549 | 109k | if (feerate_cmp != 0) return feerate_cmp < 0; Branch (1549:17): [True: 36.0k, False: 73.9k]
| 1550 | | // Then by decreasing chunk size. | 1551 | 73.9k | if (chunk_feerate_a.size != chunk_feerate_b.size) { Branch (1551:17): [True: 16.1k, False: 57.8k]
| 1552 | 16.1k | return chunk_feerate_a.size > chunk_feerate_b.size; | 1553 | 16.1k | } | 1554 | | // Tie-break by decreasing fallback_order. | 1555 | 57.8k | auto fallback_cmp = fallback_order(a.second, b.second); | 1556 | 57.8k | if (fallback_cmp != 0) return fallback_cmp > 0; Branch (1556:17): [True: 57.8k, False: 0]
| 1557 | | // This should not be hit, because fallback_order defines a strong ordering. | 1558 | 0 | Assume(false); | 1559 | 0 | return a.second < b.second; | 1560 | 57.8k | }; |
txgraph.cpp:_ZZN17cluster_linearize19SpanningForestStateIN13bitset_detail9IntBitSetImEENS_19SFLDefaultCostModelEE16GetLinearizationITkNS_16StrongComparatorIjEEZN12_GLOBAL__N_118GenericClusterImpl11RelinearizeERNS8_11TxGraphImplEimE3$_0EESt6vectorIjSaIjEERKT_ENKUlSI_RKT0_E0_clISt4pairIhjESP_EEDaSI_SL_ Line | Count | Source | 1542 | 8.60M | auto chunk_cmp_fn = [&](const auto& a, const auto& b) noexcept { | 1543 | | // Bail out for identical chunks. | 1544 | 8.60M | if (a.first == b.first) return false; Branch (1544:17): [True: 0, False: 8.60M]
| 1545 | | // First sort by increasing chunk feerate. | 1546 | 8.60M | auto& chunk_feerate_a = m_set_info[a.first].feerate; | 1547 | 8.60M | auto& chunk_feerate_b = m_set_info[b.first].feerate; | 1548 | 8.60M | auto feerate_cmp = ByRatio{chunk_feerate_a} <=> ByRatio{chunk_feerate_b}; | 1549 | 8.60M | if (feerate_cmp != 0) return feerate_cmp < 0; Branch (1549:17): [True: 4.09M, False: 4.50M]
| 1550 | | // Then by decreasing chunk size. | 1551 | 4.50M | if (chunk_feerate_a.size != chunk_feerate_b.size) { Branch (1551:17): [True: 307k, False: 4.19M]
| 1552 | 307k | return chunk_feerate_a.size > chunk_feerate_b.size; | 1553 | 307k | } | 1554 | | // Tie-break by decreasing fallback_order. | 1555 | 4.19M | auto fallback_cmp = fallback_order(a.second, b.second); | 1556 | 4.19M | if (fallback_cmp != 0) return fallback_cmp > 0; Branch (1556:17): [True: 4.19M, False: 0]
| 1557 | | // This should not be hit, because fallback_order defines a strong ordering. | 1558 | 0 | Assume(false); | 1559 | 0 | return a.second < b.second; | 1560 | 4.19M | }; |
|
1561 | | // Construct a heap with all chunks that have no out-of-chunk dependencies. |
1562 | 4.57M | for (SetIdx chunk_idx : m_chunk_idxs) { Branch (1562:31): [True: 35.7k, False: 2.63k]
Branch (1562:31): [True: 17.8k, False: 389]
Branch (1562:31): [True: 4.52M, False: 1.32M]
|
1563 | 4.57M | if (chunk_deps[chunk_idx] == 0) { Branch (1563:17): [True: 22.5k, False: 13.1k]
Branch (1563:17): [True: 13.2k, False: 4.60k]
Branch (1563:17): [True: 1.70M, False: 2.81M]
|
1564 | 1.74M | ready_chunks[num_ready_chunks++] = {chunk_idx, max_fallback_fn(chunk_idx)}; |
1565 | 1.74M | } |
1566 | 4.57M | } |
1567 | 1.32M | std::make_heap(ready_chunks.begin(), ready_chunks.begin() + num_ready_chunks, chunk_cmp_fn); |
1568 | | // Pop chunks off the heap. |
1569 | 5.89M | while (num_ready_chunks > 0) { Branch (1569:16): [True: 35.7k, False: 2.63k]
Branch (1569:16): [True: 17.8k, False: 389]
Branch (1569:16): [True: 4.52M, False: 1.32M]
|
1570 | 4.57M | auto [chunk_idx, _rnd] = ready_chunks.front(); |
1571 | 4.57M | std::pop_heap(ready_chunks.begin(), ready_chunks.begin() + num_ready_chunks, chunk_cmp_fn); |
1572 | 4.57M | --num_ready_chunks; |
1573 | 4.57M | Assume(chunk_deps[chunk_idx] == 0); |
1574 | 4.57M | const auto& chunk_txn = m_set_info[chunk_idx].transactions; |
1575 | | // Build heap of all includable transactions in chunk. |
1576 | 4.57M | Assume(num_ready_tx == 0); |
1577 | 6.75M | for (TxIdx tx_idx : chunk_txn) { Branch (1577:31): [True: 67.1k, False: 35.7k]
Branch (1577:31): [True: 21.1k, False: 17.8k]
Branch (1577:31): [True: 6.66M, False: 4.52M]
|
1578 | 6.75M | if (tx_deps[tx_idx] == 0) ready_tx[num_ready_tx++] = tx_idx; Branch (1578:21): [True: 43.4k, False: 23.7k]
Branch (1578:21): [True: 18.4k, False: 2.70k]
Branch (1578:21): [True: 4.72M, False: 1.93M]
|
1579 | 6.75M | } |
1580 | 4.57M | Assume(num_ready_tx > 0); |
1581 | 4.57M | std::make_heap(ready_tx.begin(), ready_tx.begin() + num_ready_tx, tx_cmp_fn); |
1582 | | // Pick transactions from the ready heap, append them to linearization, and decrement |
1583 | | // dependency counts. |
1584 | 11.3M | while (num_ready_tx > 0) { Branch (1584:20): [True: 67.1k, False: 35.7k]
Branch (1584:20): [True: 21.1k, False: 17.8k]
Branch (1584:20): [True: 6.66M, False: 4.52M]
|
1585 | | // Pop an element from the tx_ready heap. |
1586 | 6.75M | auto tx_idx = ready_tx.front(); |
1587 | 6.75M | std::pop_heap(ready_tx.begin(), ready_tx.begin() + num_ready_tx, tx_cmp_fn); |
1588 | 6.75M | --num_ready_tx; |
1589 | | // Append to linearization. |
1590 | 6.75M | ret.push_back(tx_idx); |
1591 | | // Decrement dependency counts. |
1592 | 6.75M | auto& tx_data = m_tx_data[tx_idx]; |
1593 | 6.75M | for (TxIdx chl_idx : tx_data.children) { Branch (1593:36): [True: 67.7k, False: 67.1k]
Branch (1593:36): [True: 9.89k, False: 21.1k]
Branch (1593:36): [True: 5.72M, False: 6.66M]
|
1594 | 5.80M | auto& chl_data = m_tx_data[chl_idx]; |
1595 | | // Decrement tx dependency count. |
1596 | 5.80M | Assume(tx_deps[chl_idx] > 0); |
1597 | 5.80M | if (--tx_deps[chl_idx] == 0 && chunk_txn[chl_idx]) { Branch (1597:25): [True: 36.0k, False: 31.7k]
Branch (1597:52): [True: 23.7k, False: 12.3k]
Branch (1597:25): [True: 7.19k, False: 2.69k]
Branch (1597:52): [True: 2.70k, False: 4.49k]
Branch (1597:25): [True: 4.68M, False: 1.03M]
Branch (1597:52): [True: 1.93M, False: 2.75M]
|
1598 | | // Child tx has no dependencies left, and is in this chunk. Add it to the tx heap. |
1599 | 1.96M | ready_tx[num_ready_tx++] = chl_idx; |
1600 | 1.96M | std::push_heap(ready_tx.begin(), ready_tx.begin() + num_ready_tx, tx_cmp_fn); |
1601 | 1.96M | } |
1602 | | // Decrement chunk dependency count if this is out-of-chunk dependency. |
1603 | 5.80M | if (chl_data.chunk_idx != chunk_idx) { Branch (1603:25): [True: 29.1k, False: 38.6k]
Branch (1603:25): [True: 6.30k, False: 3.58k]
Branch (1603:25): [True: 3.42M, False: 2.30M]
|
1604 | 3.45M | Assume(chunk_deps[chl_data.chunk_idx] > 0); |
1605 | 3.45M | if (--chunk_deps[chl_data.chunk_idx] == 0) { Branch (1605:29): [True: 13.1k, False: 15.9k]
Branch (1605:29): [True: 4.60k, False: 1.70k]
Branch (1605:29): [True: 2.81M, False: 609k]
|
1606 | | // Child chunk has no dependencies left. Add it to the chunk heap. |
1607 | 2.83M | ready_chunks[num_ready_chunks++] = {chl_data.chunk_idx, max_fallback_fn(chl_data.chunk_idx)}; |
1608 | 2.83M | std::push_heap(ready_chunks.begin(), ready_chunks.begin() + num_ready_chunks, chunk_cmp_fn); |
1609 | 2.83M | } |
1610 | 3.45M | } |
1611 | 5.80M | } |
1612 | 6.75M | } |
1613 | 4.57M | } |
1614 | 1.32M | Assume(ret.size() == m_set_info.size()); |
1615 | 1.32M | m_cost.GetLinearizationEnd(/*num_txns=*/m_set_info.size(), /*num_deps=*/num_deps); |
1616 | 1.32M | return ret; |
1617 | 1.32M | } _ZN17cluster_linearize19SpanningForestStateIN13bitset_detail9IntBitSetIjEENS_19SFLDefaultCostModelEE16GetLinearizationITkNS_16StrongComparatorIjEESt17compare_three_wayEESt6vectorIjSaIjEERKT_ Line | Count | Source | 1472 | 2.63k | { | 1473 | 2.63k | m_cost.GetLinearizationBegin(); | 1474 | | /** The output linearization. */ | 1475 | 2.63k | std::vector<DepGraphIndex> ret; | 1476 | 2.63k | ret.reserve(m_set_info.size()); | 1477 | | /** A heap with all chunks (by set index) that can currently be included, sorted by | 1478 | | * chunk feerate (high to low), chunk size (small to large), and by least maximum element | 1479 | | * according to the fallback order (which is the second pair element). */ | 1480 | 2.63k | std::array<std::pair<SetIdx, TxIdx>, SetType::Size()> ready_chunks; | 1481 | | /** The number of entries of ready_chunks in use. */ | 1482 | 2.63k | unsigned num_ready_chunks{0}; | 1483 | | /** For every chunk, indexed by SetIdx, the number of unmet dependencies the chunk has on | 1484 | | * other chunks (not including dependencies within the chunk itself). */ | 1485 | 2.63k | std::array<TxIdx, SetType::Size()> chunk_deps; | 1486 | 2.63k | std::fill_n(chunk_deps.begin(), m_set_info.size(), TxIdx{0}); | 1487 | | /** For every transaction, indexed by TxIdx, the number of unmet dependencies the | 1488 | | * transaction has. */ | 1489 | 2.63k | std::array<TxIdx, SetType::Size()> tx_deps; | 1490 | 2.63k | std::fill_n(tx_deps.begin(), m_tx_data.size(), TxIdx{0}); | 1491 | | /** A heap with all transactions within the current chunk that can be included, sorted by | 1492 | | * tx feerate (high to low), tx size (small to large), and fallback order. */ | 1493 | 2.63k | std::array<TxIdx, SetType::Size()> ready_tx; | 1494 | | /** The number of entries of ready_tx in use. */ | 1495 | 2.63k | unsigned num_ready_tx{0}; | 1496 | | // Populate chunk_deps and tx_deps. | 1497 | 2.63k | unsigned num_deps{0}; | 1498 | 67.1k | for (TxIdx chl_idx : m_transaction_idxs) { Branch (1498:28): [True: 67.1k, False: 2.63k]
| 1499 | 67.1k | const auto& chl_data = m_tx_data[chl_idx]; | 1500 | 67.1k | tx_deps[chl_idx] = chl_data.parents.Count(); | 1501 | 67.1k | num_deps += tx_deps[chl_idx]; | 1502 | 67.1k | auto chl_chunk_idx = chl_data.chunk_idx; | 1503 | 67.1k | auto& chl_chunk_info = m_set_info[chl_chunk_idx]; | 1504 | 67.1k | chunk_deps[chl_chunk_idx] += (chl_data.parents - chl_chunk_info.transactions).Count(); | 1505 | 67.1k | } | 1506 | | /** Function to compute the highest element of a chunk, by fallback_order. */ | 1507 | 2.63k | auto max_fallback_fn = [&](SetIdx chunk_idx) noexcept { | 1508 | 2.63k | auto& chunk = m_set_info[chunk_idx].transactions; | 1509 | 2.63k | auto it = chunk.begin(); | 1510 | 2.63k | DepGraphIndex ret = *it; | 1511 | 2.63k | ++it; | 1512 | 2.63k | while (it != chunk.end()) { | 1513 | 2.63k | if (fallback_order(*it, ret) > 0) ret = *it; | 1514 | 2.63k | ++it; | 1515 | 2.63k | } | 1516 | 2.63k | return ret; | 1517 | 2.63k | }; | 1518 | | /** Comparison function for the transaction heap. Note that it is a max-heap, so | 1519 | | * tx_cmp_fn(a, b) == true means "a appears after b in the linearization". */ | 1520 | 2.63k | auto tx_cmp_fn = [&](const auto& a, const auto& b) noexcept { | 1521 | | // Bail out for identical transactions. | 1522 | 2.63k | if (a == b) return false; | 1523 | | // First sort by increasing transaction feerate. | 1524 | 2.63k | auto& a_feerate = m_depgraph.FeeRate(a); | 1525 | 2.63k | auto& b_feerate = m_depgraph.FeeRate(b); | 1526 | 2.63k | auto feerate_cmp = ByRatio{a_feerate} <=> ByRatio{b_feerate}; | 1527 | 2.63k | if (feerate_cmp != 0) return feerate_cmp < 0; | 1528 | | // Then by decreasing transaction size. | 1529 | 2.63k | if (a_feerate.size != b_feerate.size) { | 1530 | 2.63k | return a_feerate.size > b_feerate.size; | 1531 | 2.63k | } | 1532 | | // Tie-break by decreasing fallback_order. | 1533 | 2.63k | auto fallback_cmp = fallback_order(a, b); | 1534 | 2.63k | if (fallback_cmp != 0) return fallback_cmp > 0; | 1535 | | // This should not be hit, because fallback_order defines a strong ordering. | 1536 | 2.63k | Assume(false); | 1537 | 2.63k | return a < b; | 1538 | 2.63k | }; | 1539 | | // Construct a heap with all chunks that have no out-of-chunk dependencies. | 1540 | | /** Comparison function for the chunk heap. Note that it is a max-heap, so | 1541 | | * chunk_cmp_fn(a, b) == true means "a appears after b in the linearization". */ | 1542 | 2.63k | auto chunk_cmp_fn = [&](const auto& a, const auto& b) noexcept { | 1543 | | // Bail out for identical chunks. | 1544 | 2.63k | if (a.first == b.first) return false; | 1545 | | // First sort by increasing chunk feerate. | 1546 | 2.63k | auto& chunk_feerate_a = m_set_info[a.first].feerate; | 1547 | 2.63k | auto& chunk_feerate_b = m_set_info[b.first].feerate; | 1548 | 2.63k | auto feerate_cmp = ByRatio{chunk_feerate_a} <=> ByRatio{chunk_feerate_b}; | 1549 | 2.63k | if (feerate_cmp != 0) return feerate_cmp < 0; | 1550 | | // Then by decreasing chunk size. | 1551 | 2.63k | if (chunk_feerate_a.size != chunk_feerate_b.size) { | 1552 | 2.63k | return chunk_feerate_a.size > chunk_feerate_b.size; | 1553 | 2.63k | } | 1554 | | // Tie-break by decreasing fallback_order. | 1555 | 2.63k | auto fallback_cmp = fallback_order(a.second, b.second); | 1556 | 2.63k | if (fallback_cmp != 0) return fallback_cmp > 0; | 1557 | | // This should not be hit, because fallback_order defines a strong ordering. | 1558 | 2.63k | Assume(false); | 1559 | 2.63k | return a.second < b.second; | 1560 | 2.63k | }; | 1561 | | // Construct a heap with all chunks that have no out-of-chunk dependencies. | 1562 | 35.7k | for (SetIdx chunk_idx : m_chunk_idxs) { Branch (1562:31): [True: 35.7k, False: 2.63k]
| 1563 | 35.7k | if (chunk_deps[chunk_idx] == 0) { Branch (1563:17): [True: 22.5k, False: 13.1k]
| 1564 | 22.5k | ready_chunks[num_ready_chunks++] = {chunk_idx, max_fallback_fn(chunk_idx)}; | 1565 | 22.5k | } | 1566 | 35.7k | } | 1567 | 2.63k | std::make_heap(ready_chunks.begin(), ready_chunks.begin() + num_ready_chunks, chunk_cmp_fn); | 1568 | | // Pop chunks off the heap. | 1569 | 38.3k | while (num_ready_chunks > 0) { Branch (1569:16): [True: 35.7k, False: 2.63k]
| 1570 | 35.7k | auto [chunk_idx, _rnd] = ready_chunks.front(); | 1571 | 35.7k | std::pop_heap(ready_chunks.begin(), ready_chunks.begin() + num_ready_chunks, chunk_cmp_fn); | 1572 | 35.7k | --num_ready_chunks; | 1573 | 35.7k | Assume(chunk_deps[chunk_idx] == 0); | 1574 | 35.7k | const auto& chunk_txn = m_set_info[chunk_idx].transactions; | 1575 | | // Build heap of all includable transactions in chunk. | 1576 | 35.7k | Assume(num_ready_tx == 0); | 1577 | 67.1k | for (TxIdx tx_idx : chunk_txn) { Branch (1577:31): [True: 67.1k, False: 35.7k]
| 1578 | 67.1k | if (tx_deps[tx_idx] == 0) ready_tx[num_ready_tx++] = tx_idx; Branch (1578:21): [True: 43.4k, False: 23.7k]
| 1579 | 67.1k | } | 1580 | 35.7k | Assume(num_ready_tx > 0); | 1581 | 35.7k | std::make_heap(ready_tx.begin(), ready_tx.begin() + num_ready_tx, tx_cmp_fn); | 1582 | | // Pick transactions from the ready heap, append them to linearization, and decrement | 1583 | | // dependency counts. | 1584 | 102k | while (num_ready_tx > 0) { Branch (1584:20): [True: 67.1k, False: 35.7k]
| 1585 | | // Pop an element from the tx_ready heap. | 1586 | 67.1k | auto tx_idx = ready_tx.front(); | 1587 | 67.1k | std::pop_heap(ready_tx.begin(), ready_tx.begin() + num_ready_tx, tx_cmp_fn); | 1588 | 67.1k | --num_ready_tx; | 1589 | | // Append to linearization. | 1590 | 67.1k | ret.push_back(tx_idx); | 1591 | | // Decrement dependency counts. | 1592 | 67.1k | auto& tx_data = m_tx_data[tx_idx]; | 1593 | 67.7k | for (TxIdx chl_idx : tx_data.children) { Branch (1593:36): [True: 67.7k, False: 67.1k]
| 1594 | 67.7k | auto& chl_data = m_tx_data[chl_idx]; | 1595 | | // Decrement tx dependency count. | 1596 | 67.7k | Assume(tx_deps[chl_idx] > 0); | 1597 | 67.7k | if (--tx_deps[chl_idx] == 0 && chunk_txn[chl_idx]) { Branch (1597:25): [True: 36.0k, False: 31.7k]
Branch (1597:52): [True: 23.7k, False: 12.3k]
| 1598 | | // Child tx has no dependencies left, and is in this chunk. Add it to the tx heap. | 1599 | 23.7k | ready_tx[num_ready_tx++] = chl_idx; | 1600 | 23.7k | std::push_heap(ready_tx.begin(), ready_tx.begin() + num_ready_tx, tx_cmp_fn); | 1601 | 23.7k | } | 1602 | | // Decrement chunk dependency count if this is out-of-chunk dependency. | 1603 | 67.7k | if (chl_data.chunk_idx != chunk_idx) { Branch (1603:25): [True: 29.1k, False: 38.6k]
| 1604 | 29.1k | Assume(chunk_deps[chl_data.chunk_idx] > 0); | 1605 | 29.1k | if (--chunk_deps[chl_data.chunk_idx] == 0) { Branch (1605:29): [True: 13.1k, False: 15.9k]
| 1606 | | // Child chunk has no dependencies left. Add it to the chunk heap. | 1607 | 13.1k | ready_chunks[num_ready_chunks++] = {chl_data.chunk_idx, max_fallback_fn(chl_data.chunk_idx)}; | 1608 | 13.1k | std::push_heap(ready_chunks.begin(), ready_chunks.begin() + num_ready_chunks, chunk_cmp_fn); | 1609 | 13.1k | } | 1610 | 29.1k | } | 1611 | 67.7k | } | 1612 | 67.1k | } | 1613 | 35.7k | } | 1614 | 2.63k | Assume(ret.size() == m_set_info.size()); | 1615 | 2.63k | m_cost.GetLinearizationEnd(/*num_txns=*/m_set_info.size(), /*num_deps=*/num_deps); | 1616 | 2.63k | return ret; | 1617 | 2.63k | } |
txgraph.cpp:_ZN17cluster_linearize19SpanningForestStateIN13bitset_detail14MultiIntBitSetImLj2EEENS_19SFLDefaultCostModelEE16GetLinearizationITkNS_16StrongComparatorIjEEZ19txgraph_fuzz_targetSt4spanIKhLm18446744073709551615EEE3$_4EESt6vectorIjSaIjEERKT_ Line | Count | Source | 1472 | 389 | { | 1473 | 389 | m_cost.GetLinearizationBegin(); | 1474 | | /** The output linearization. */ | 1475 | 389 | std::vector<DepGraphIndex> ret; | 1476 | 389 | ret.reserve(m_set_info.size()); | 1477 | | /** A heap with all chunks (by set index) that can currently be included, sorted by | 1478 | | * chunk feerate (high to low), chunk size (small to large), and by least maximum element | 1479 | | * according to the fallback order (which is the second pair element). */ | 1480 | 389 | std::array<std::pair<SetIdx, TxIdx>, SetType::Size()> ready_chunks; | 1481 | | /** The number of entries of ready_chunks in use. */ | 1482 | 389 | unsigned num_ready_chunks{0}; | 1483 | | /** For every chunk, indexed by SetIdx, the number of unmet dependencies the chunk has on | 1484 | | * other chunks (not including dependencies within the chunk itself). */ | 1485 | 389 | std::array<TxIdx, SetType::Size()> chunk_deps; | 1486 | 389 | std::fill_n(chunk_deps.begin(), m_set_info.size(), TxIdx{0}); | 1487 | | /** For every transaction, indexed by TxIdx, the number of unmet dependencies the | 1488 | | * transaction has. */ | 1489 | 389 | std::array<TxIdx, SetType::Size()> tx_deps; | 1490 | 389 | std::fill_n(tx_deps.begin(), m_tx_data.size(), TxIdx{0}); | 1491 | | /** A heap with all transactions within the current chunk that can be included, sorted by | 1492 | | * tx feerate (high to low), tx size (small to large), and fallback order. */ | 1493 | 389 | std::array<TxIdx, SetType::Size()> ready_tx; | 1494 | | /** The number of entries of ready_tx in use. */ | 1495 | 389 | unsigned num_ready_tx{0}; | 1496 | | // Populate chunk_deps and tx_deps. | 1497 | 389 | unsigned num_deps{0}; | 1498 | 21.1k | for (TxIdx chl_idx : m_transaction_idxs) { Branch (1498:28): [True: 21.1k, False: 389]
| 1499 | 21.1k | const auto& chl_data = m_tx_data[chl_idx]; | 1500 | 21.1k | tx_deps[chl_idx] = chl_data.parents.Count(); | 1501 | 21.1k | num_deps += tx_deps[chl_idx]; | 1502 | 21.1k | auto chl_chunk_idx = chl_data.chunk_idx; | 1503 | 21.1k | auto& chl_chunk_info = m_set_info[chl_chunk_idx]; | 1504 | 21.1k | chunk_deps[chl_chunk_idx] += (chl_data.parents - chl_chunk_info.transactions).Count(); | 1505 | 21.1k | } | 1506 | | /** Function to compute the highest element of a chunk, by fallback_order. */ | 1507 | 389 | auto max_fallback_fn = [&](SetIdx chunk_idx) noexcept { | 1508 | 389 | auto& chunk = m_set_info[chunk_idx].transactions; | 1509 | 389 | auto it = chunk.begin(); | 1510 | 389 | DepGraphIndex ret = *it; | 1511 | 389 | ++it; | 1512 | 389 | while (it != chunk.end()) { | 1513 | 389 | if (fallback_order(*it, ret) > 0) ret = *it; | 1514 | 389 | ++it; | 1515 | 389 | } | 1516 | 389 | return ret; | 1517 | 389 | }; | 1518 | | /** Comparison function for the transaction heap. Note that it is a max-heap, so | 1519 | | * tx_cmp_fn(a, b) == true means "a appears after b in the linearization". */ | 1520 | 389 | auto tx_cmp_fn = [&](const auto& a, const auto& b) noexcept { | 1521 | | // Bail out for identical transactions. | 1522 | 389 | if (a == b) return false; | 1523 | | // First sort by increasing transaction feerate. | 1524 | 389 | auto& a_feerate = m_depgraph.FeeRate(a); | 1525 | 389 | auto& b_feerate = m_depgraph.FeeRate(b); | 1526 | 389 | auto feerate_cmp = ByRatio{a_feerate} <=> ByRatio{b_feerate}; | 1527 | 389 | if (feerate_cmp != 0) return feerate_cmp < 0; | 1528 | | // Then by decreasing transaction size. | 1529 | 389 | if (a_feerate.size != b_feerate.size) { | 1530 | 389 | return a_feerate.size > b_feerate.size; | 1531 | 389 | } | 1532 | | // Tie-break by decreasing fallback_order. | 1533 | 389 | auto fallback_cmp = fallback_order(a, b); | 1534 | 389 | if (fallback_cmp != 0) return fallback_cmp > 0; | 1535 | | // This should not be hit, because fallback_order defines a strong ordering. | 1536 | 389 | Assume(false); | 1537 | 389 | return a < b; | 1538 | 389 | }; | 1539 | | // Construct a heap with all chunks that have no out-of-chunk dependencies. | 1540 | | /** Comparison function for the chunk heap. Note that it is a max-heap, so | 1541 | | * chunk_cmp_fn(a, b) == true means "a appears after b in the linearization". */ | 1542 | 389 | auto chunk_cmp_fn = [&](const auto& a, const auto& b) noexcept { | 1543 | | // Bail out for identical chunks. | 1544 | 389 | if (a.first == b.first) return false; | 1545 | | // First sort by increasing chunk feerate. | 1546 | 389 | auto& chunk_feerate_a = m_set_info[a.first].feerate; | 1547 | 389 | auto& chunk_feerate_b = m_set_info[b.first].feerate; | 1548 | 389 | auto feerate_cmp = ByRatio{chunk_feerate_a} <=> ByRatio{chunk_feerate_b}; | 1549 | 389 | if (feerate_cmp != 0) return feerate_cmp < 0; | 1550 | | // Then by decreasing chunk size. | 1551 | 389 | if (chunk_feerate_a.size != chunk_feerate_b.size) { | 1552 | 389 | return chunk_feerate_a.size > chunk_feerate_b.size; | 1553 | 389 | } | 1554 | | // Tie-break by decreasing fallback_order. | 1555 | 389 | auto fallback_cmp = fallback_order(a.second, b.second); | 1556 | 389 | if (fallback_cmp != 0) return fallback_cmp > 0; | 1557 | | // This should not be hit, because fallback_order defines a strong ordering. | 1558 | 389 | Assume(false); | 1559 | 389 | return a.second < b.second; | 1560 | 389 | }; | 1561 | | // Construct a heap with all chunks that have no out-of-chunk dependencies. | 1562 | 17.8k | for (SetIdx chunk_idx : m_chunk_idxs) { Branch (1562:31): [True: 17.8k, False: 389]
| 1563 | 17.8k | if (chunk_deps[chunk_idx] == 0) { Branch (1563:17): [True: 13.2k, False: 4.60k]
| 1564 | 13.2k | ready_chunks[num_ready_chunks++] = {chunk_idx, max_fallback_fn(chunk_idx)}; | 1565 | 13.2k | } | 1566 | 17.8k | } | 1567 | 389 | std::make_heap(ready_chunks.begin(), ready_chunks.begin() + num_ready_chunks, chunk_cmp_fn); | 1568 | | // Pop chunks off the heap. | 1569 | 18.2k | while (num_ready_chunks > 0) { Branch (1569:16): [True: 17.8k, False: 389]
| 1570 | 17.8k | auto [chunk_idx, _rnd] = ready_chunks.front(); | 1571 | 17.8k | std::pop_heap(ready_chunks.begin(), ready_chunks.begin() + num_ready_chunks, chunk_cmp_fn); | 1572 | 17.8k | --num_ready_chunks; | 1573 | 17.8k | Assume(chunk_deps[chunk_idx] == 0); | 1574 | 17.8k | const auto& chunk_txn = m_set_info[chunk_idx].transactions; | 1575 | | // Build heap of all includable transactions in chunk. | 1576 | 17.8k | Assume(num_ready_tx == 0); | 1577 | 21.1k | for (TxIdx tx_idx : chunk_txn) { Branch (1577:31): [True: 21.1k, False: 17.8k]
| 1578 | 21.1k | if (tx_deps[tx_idx] == 0) ready_tx[num_ready_tx++] = tx_idx; Branch (1578:21): [True: 18.4k, False: 2.70k]
| 1579 | 21.1k | } | 1580 | 17.8k | Assume(num_ready_tx > 0); | 1581 | 17.8k | std::make_heap(ready_tx.begin(), ready_tx.begin() + num_ready_tx, tx_cmp_fn); | 1582 | | // Pick transactions from the ready heap, append them to linearization, and decrement | 1583 | | // dependency counts. | 1584 | 38.9k | while (num_ready_tx > 0) { Branch (1584:20): [True: 21.1k, False: 17.8k]
| 1585 | | // Pop an element from the tx_ready heap. | 1586 | 21.1k | auto tx_idx = ready_tx.front(); | 1587 | 21.1k | std::pop_heap(ready_tx.begin(), ready_tx.begin() + num_ready_tx, tx_cmp_fn); | 1588 | 21.1k | --num_ready_tx; | 1589 | | // Append to linearization. | 1590 | 21.1k | ret.push_back(tx_idx); | 1591 | | // Decrement dependency counts. | 1592 | 21.1k | auto& tx_data = m_tx_data[tx_idx]; | 1593 | 21.1k | for (TxIdx chl_idx : tx_data.children) { Branch (1593:36): [True: 9.89k, False: 21.1k]
| 1594 | 9.89k | auto& chl_data = m_tx_data[chl_idx]; | 1595 | | // Decrement tx dependency count. | 1596 | 9.89k | Assume(tx_deps[chl_idx] > 0); | 1597 | 9.89k | if (--tx_deps[chl_idx] == 0 && chunk_txn[chl_idx]) { Branch (1597:25): [True: 7.19k, False: 2.69k]
Branch (1597:52): [True: 2.70k, False: 4.49k]
| 1598 | | // Child tx has no dependencies left, and is in this chunk. Add it to the tx heap. | 1599 | 2.70k | ready_tx[num_ready_tx++] = chl_idx; | 1600 | 2.70k | std::push_heap(ready_tx.begin(), ready_tx.begin() + num_ready_tx, tx_cmp_fn); | 1601 | 2.70k | } | 1602 | | // Decrement chunk dependency count if this is out-of-chunk dependency. | 1603 | 9.89k | if (chl_data.chunk_idx != chunk_idx) { Branch (1603:25): [True: 6.30k, False: 3.58k]
| 1604 | 6.30k | Assume(chunk_deps[chl_data.chunk_idx] > 0); | 1605 | 6.30k | if (--chunk_deps[chl_data.chunk_idx] == 0) { Branch (1605:29): [True: 4.60k, False: 1.70k]
| 1606 | | // Child chunk has no dependencies left. Add it to the chunk heap. | 1607 | 4.60k | ready_chunks[num_ready_chunks++] = {chl_data.chunk_idx, max_fallback_fn(chl_data.chunk_idx)}; | 1608 | 4.60k | std::push_heap(ready_chunks.begin(), ready_chunks.begin() + num_ready_chunks, chunk_cmp_fn); | 1609 | 4.60k | } | 1610 | 6.30k | } | 1611 | 9.89k | } | 1612 | 21.1k | } | 1613 | 17.8k | } | 1614 | 389 | Assume(ret.size() == m_set_info.size()); | 1615 | 389 | m_cost.GetLinearizationEnd(/*num_txns=*/m_set_info.size(), /*num_deps=*/num_deps); | 1616 | 389 | return ret; | 1617 | 389 | } |
txgraph.cpp:_ZN17cluster_linearize19SpanningForestStateIN13bitset_detail9IntBitSetImEENS_19SFLDefaultCostModelEE16GetLinearizationITkNS_16StrongComparatorIjEEZN12_GLOBAL__N_118GenericClusterImpl11RelinearizeERNS8_11TxGraphImplEimE3$_0EESt6vectorIjSaIjEERKT_ Line | Count | Source | 1472 | 1.32M | { | 1473 | 1.32M | m_cost.GetLinearizationBegin(); | 1474 | | /** The output linearization. */ | 1475 | 1.32M | std::vector<DepGraphIndex> ret; | 1476 | 1.32M | ret.reserve(m_set_info.size()); | 1477 | | /** A heap with all chunks (by set index) that can currently be included, sorted by | 1478 | | * chunk feerate (high to low), chunk size (small to large), and by least maximum element | 1479 | | * according to the fallback order (which is the second pair element). */ | 1480 | 1.32M | std::array<std::pair<SetIdx, TxIdx>, SetType::Size()> ready_chunks; | 1481 | | /** The number of entries of ready_chunks in use. */ | 1482 | 1.32M | unsigned num_ready_chunks{0}; | 1483 | | /** For every chunk, indexed by SetIdx, the number of unmet dependencies the chunk has on | 1484 | | * other chunks (not including dependencies within the chunk itself). */ | 1485 | 1.32M | std::array<TxIdx, SetType::Size()> chunk_deps; | 1486 | 1.32M | std::fill_n(chunk_deps.begin(), m_set_info.size(), TxIdx{0}); | 1487 | | /** For every transaction, indexed by TxIdx, the number of unmet dependencies the | 1488 | | * transaction has. */ | 1489 | 1.32M | std::array<TxIdx, SetType::Size()> tx_deps; | 1490 | 1.32M | std::fill_n(tx_deps.begin(), m_tx_data.size(), TxIdx{0}); | 1491 | | /** A heap with all transactions within the current chunk that can be included, sorted by | 1492 | | * tx feerate (high to low), tx size (small to large), and fallback order. */ | 1493 | 1.32M | std::array<TxIdx, SetType::Size()> ready_tx; | 1494 | | /** The number of entries of ready_tx in use. */ | 1495 | 1.32M | unsigned num_ready_tx{0}; | 1496 | | // Populate chunk_deps and tx_deps. | 1497 | 1.32M | unsigned num_deps{0}; | 1498 | 6.66M | for (TxIdx chl_idx : m_transaction_idxs) { Branch (1498:28): [True: 6.66M, False: 1.32M]
| 1499 | 6.66M | const auto& chl_data = m_tx_data[chl_idx]; | 1500 | 6.66M | tx_deps[chl_idx] = chl_data.parents.Count(); | 1501 | 6.66M | num_deps += tx_deps[chl_idx]; | 1502 | 6.66M | auto chl_chunk_idx = chl_data.chunk_idx; | 1503 | 6.66M | auto& chl_chunk_info = m_set_info[chl_chunk_idx]; | 1504 | 6.66M | chunk_deps[chl_chunk_idx] += (chl_data.parents - chl_chunk_info.transactions).Count(); | 1505 | 6.66M | } | 1506 | | /** Function to compute the highest element of a chunk, by fallback_order. */ | 1507 | 1.32M | auto max_fallback_fn = [&](SetIdx chunk_idx) noexcept { | 1508 | 1.32M | auto& chunk = m_set_info[chunk_idx].transactions; | 1509 | 1.32M | auto it = chunk.begin(); | 1510 | 1.32M | DepGraphIndex ret = *it; | 1511 | 1.32M | ++it; | 1512 | 1.32M | while (it != chunk.end()) { | 1513 | 1.32M | if (fallback_order(*it, ret) > 0) ret = *it; | 1514 | 1.32M | ++it; | 1515 | 1.32M | } | 1516 | 1.32M | return ret; | 1517 | 1.32M | }; | 1518 | | /** Comparison function for the transaction heap. Note that it is a max-heap, so | 1519 | | * tx_cmp_fn(a, b) == true means "a appears after b in the linearization". */ | 1520 | 1.32M | auto tx_cmp_fn = [&](const auto& a, const auto& b) noexcept { | 1521 | | // Bail out for identical transactions. | 1522 | 1.32M | if (a == b) return false; | 1523 | | // First sort by increasing transaction feerate. | 1524 | 1.32M | auto& a_feerate = m_depgraph.FeeRate(a); | 1525 | 1.32M | auto& b_feerate = m_depgraph.FeeRate(b); | 1526 | 1.32M | auto feerate_cmp = ByRatio{a_feerate} <=> ByRatio{b_feerate}; | 1527 | 1.32M | if (feerate_cmp != 0) return feerate_cmp < 0; | 1528 | | // Then by decreasing transaction size. | 1529 | 1.32M | if (a_feerate.size != b_feerate.size) { | 1530 | 1.32M | return a_feerate.size > b_feerate.size; | 1531 | 1.32M | } | 1532 | | // Tie-break by decreasing fallback_order. | 1533 | 1.32M | auto fallback_cmp = fallback_order(a, b); | 1534 | 1.32M | if (fallback_cmp != 0) return fallback_cmp > 0; | 1535 | | // This should not be hit, because fallback_order defines a strong ordering. | 1536 | 1.32M | Assume(false); | 1537 | 1.32M | return a < b; | 1538 | 1.32M | }; | 1539 | | // Construct a heap with all chunks that have no out-of-chunk dependencies. | 1540 | | /** Comparison function for the chunk heap. Note that it is a max-heap, so | 1541 | | * chunk_cmp_fn(a, b) == true means "a appears after b in the linearization". */ | 1542 | 1.32M | auto chunk_cmp_fn = [&](const auto& a, const auto& b) noexcept { | 1543 | | // Bail out for identical chunks. | 1544 | 1.32M | if (a.first == b.first) return false; | 1545 | | // First sort by increasing chunk feerate. | 1546 | 1.32M | auto& chunk_feerate_a = m_set_info[a.first].feerate; | 1547 | 1.32M | auto& chunk_feerate_b = m_set_info[b.first].feerate; | 1548 | 1.32M | auto feerate_cmp = ByRatio{chunk_feerate_a} <=> ByRatio{chunk_feerate_b}; | 1549 | 1.32M | if (feerate_cmp != 0) return feerate_cmp < 0; | 1550 | | // Then by decreasing chunk size. | 1551 | 1.32M | if (chunk_feerate_a.size != chunk_feerate_b.size) { | 1552 | 1.32M | return chunk_feerate_a.size > chunk_feerate_b.size; | 1553 | 1.32M | } | 1554 | | // Tie-break by decreasing fallback_order. | 1555 | 1.32M | auto fallback_cmp = fallback_order(a.second, b.second); | 1556 | 1.32M | if (fallback_cmp != 0) return fallback_cmp > 0; | 1557 | | // This should not be hit, because fallback_order defines a strong ordering. | 1558 | 1.32M | Assume(false); | 1559 | 1.32M | return a.second < b.second; | 1560 | 1.32M | }; | 1561 | | // Construct a heap with all chunks that have no out-of-chunk dependencies. | 1562 | 4.52M | for (SetIdx chunk_idx : m_chunk_idxs) { Branch (1562:31): [True: 4.52M, False: 1.32M]
| 1563 | 4.52M | if (chunk_deps[chunk_idx] == 0) { Branch (1563:17): [True: 1.70M, False: 2.81M]
| 1564 | 1.70M | ready_chunks[num_ready_chunks++] = {chunk_idx, max_fallback_fn(chunk_idx)}; | 1565 | 1.70M | } | 1566 | 4.52M | } | 1567 | 1.32M | std::make_heap(ready_chunks.begin(), ready_chunks.begin() + num_ready_chunks, chunk_cmp_fn); | 1568 | | // Pop chunks off the heap. | 1569 | 5.84M | while (num_ready_chunks > 0) { Branch (1569:16): [True: 4.52M, False: 1.32M]
| 1570 | 4.52M | auto [chunk_idx, _rnd] = ready_chunks.front(); | 1571 | 4.52M | std::pop_heap(ready_chunks.begin(), ready_chunks.begin() + num_ready_chunks, chunk_cmp_fn); | 1572 | 4.52M | --num_ready_chunks; | 1573 | 4.52M | Assume(chunk_deps[chunk_idx] == 0); | 1574 | 4.52M | const auto& chunk_txn = m_set_info[chunk_idx].transactions; | 1575 | | // Build heap of all includable transactions in chunk. | 1576 | 4.52M | Assume(num_ready_tx == 0); | 1577 | 6.66M | for (TxIdx tx_idx : chunk_txn) { Branch (1577:31): [True: 6.66M, False: 4.52M]
| 1578 | 6.66M | if (tx_deps[tx_idx] == 0) ready_tx[num_ready_tx++] = tx_idx; Branch (1578:21): [True: 4.72M, False: 1.93M]
| 1579 | 6.66M | } | 1580 | 4.52M | Assume(num_ready_tx > 0); | 1581 | 4.52M | std::make_heap(ready_tx.begin(), ready_tx.begin() + num_ready_tx, tx_cmp_fn); | 1582 | | // Pick transactions from the ready heap, append them to linearization, and decrement | 1583 | | // dependency counts. | 1584 | 11.1M | while (num_ready_tx > 0) { Branch (1584:20): [True: 6.66M, False: 4.52M]
| 1585 | | // Pop an element from the tx_ready heap. | 1586 | 6.66M | auto tx_idx = ready_tx.front(); | 1587 | 6.66M | std::pop_heap(ready_tx.begin(), ready_tx.begin() + num_ready_tx, tx_cmp_fn); | 1588 | 6.66M | --num_ready_tx; | 1589 | | // Append to linearization. | 1590 | 6.66M | ret.push_back(tx_idx); | 1591 | | // Decrement dependency counts. | 1592 | 6.66M | auto& tx_data = m_tx_data[tx_idx]; | 1593 | 6.66M | for (TxIdx chl_idx : tx_data.children) { Branch (1593:36): [True: 5.72M, False: 6.66M]
| 1594 | 5.72M | auto& chl_data = m_tx_data[chl_idx]; | 1595 | | // Decrement tx dependency count. | 1596 | 5.72M | Assume(tx_deps[chl_idx] > 0); | 1597 | 5.72M | if (--tx_deps[chl_idx] == 0 && chunk_txn[chl_idx]) { Branch (1597:25): [True: 4.68M, False: 1.03M]
Branch (1597:52): [True: 1.93M, False: 2.75M]
| 1598 | | // Child tx has no dependencies left, and is in this chunk. Add it to the tx heap. | 1599 | 1.93M | ready_tx[num_ready_tx++] = chl_idx; | 1600 | 1.93M | std::push_heap(ready_tx.begin(), ready_tx.begin() + num_ready_tx, tx_cmp_fn); | 1601 | 1.93M | } | 1602 | | // Decrement chunk dependency count if this is out-of-chunk dependency. | 1603 | 5.72M | if (chl_data.chunk_idx != chunk_idx) { Branch (1603:25): [True: 3.42M, False: 2.30M]
| 1604 | 3.42M | Assume(chunk_deps[chl_data.chunk_idx] > 0); | 1605 | 3.42M | if (--chunk_deps[chl_data.chunk_idx] == 0) { Branch (1605:29): [True: 2.81M, False: 609k]
| 1606 | | // Child chunk has no dependencies left. Add it to the chunk heap. | 1607 | 2.81M | ready_chunks[num_ready_chunks++] = {chl_data.chunk_idx, max_fallback_fn(chl_data.chunk_idx)}; | 1608 | 2.81M | std::push_heap(ready_chunks.begin(), ready_chunks.begin() + num_ready_chunks, chunk_cmp_fn); | 1609 | 2.81M | } | 1610 | 3.42M | } | 1611 | 5.72M | } | 1612 | 6.66M | } | 1613 | 4.52M | } | 1614 | 1.32M | Assume(ret.size() == m_set_info.size()); | 1615 | 1.32M | m_cost.GetLinearizationEnd(/*num_txns=*/m_set_info.size(), /*num_deps=*/num_deps); | 1616 | 1.32M | return ret; | 1617 | 1.32M | } |
|
1618 | | |
1619 | | /** Get the diagram for the current state, which must be topological. Test-only. |
1620 | | * |
1621 | | * The linearization produced by GetLinearization() is always at least as good (in the |
1622 | | * CompareChunks() sense) as this diagram, but may be better. |
1623 | | * |
1624 | | * After an OptimizeStep(), the diagram will always be at least as good as before. Once |
1625 | | * OptimizeStep() returns false, the diagram will be equivalent to that produced by |
1626 | | * GetLinearization(), and optimal. |
1627 | | * |
1628 | | * After a MinimizeStep(), the diagram cannot change anymore (in the CompareChunks() sense), |
1629 | | * but its number of segments can increase still. Once MinimizeStep() returns false, the number |
1630 | | * of chunks of the produced linearization will match the number of segments in the diagram. |
1631 | | */ |
1632 | | std::vector<FeeFrac> GetDiagram() const noexcept |
1633 | 19.3k | { |
1634 | 19.3k | std::vector<FeeFrac> ret; |
1635 | 298k | for (auto chunk_idx : m_chunk_idxs) { Branch (1635:29): [True: 298k, False: 19.3k]
|
1636 | 298k | ret.push_back(m_set_info[chunk_idx].feerate); |
1637 | 298k | } |
1638 | 19.3k | std::ranges::sort(ret, std::greater<ByRatioNegSize<FeeFrac>>{}); |
1639 | 19.3k | return ret; |
1640 | 19.3k | } |
1641 | | |
1642 | | /** Determine how much work was performed so far. */ |
1643 | 11.9M | uint64_t GetCost() const noexcept { return m_cost.GetCost(); }_ZNK17cluster_linearize19SpanningForestStateIN13bitset_detail9IntBitSetIjEENS_19SFLDefaultCostModelEE7GetCostEv Line | Count | Source | 1643 | 21.6k | uint64_t GetCost() const noexcept { return m_cost.GetCost(); } |
_ZNK17cluster_linearize19SpanningForestStateIN13bitset_detail14MultiIntBitSetImLj2EEENS_19SFLDefaultCostModelEE7GetCostEv Line | Count | Source | 1643 | 38.1k | uint64_t GetCost() const noexcept { return m_cost.GetCost(); } |
_ZNK17cluster_linearize19SpanningForestStateIN13bitset_detail9IntBitSetImEENS_19SFLDefaultCostModelEE7GetCostEv Line | Count | Source | 1643 | 11.8M | uint64_t GetCost() const noexcept { return m_cost.GetCost(); } |
|
1644 | | |
1645 | | /** Verify internal consistency of the data structure. */ |
1646 | | void SanityCheck() const |
1647 | 1.83k | { |
1648 | | // |
1649 | | // Verify dependency parent/child information, and build list of (active) dependencies. |
1650 | | // |
1651 | 1.83k | std::vector<std::pair<TxIdx, TxIdx>> expected_dependencies; |
1652 | 1.83k | std::vector<std::pair<TxIdx, TxIdx>> all_dependencies; |
1653 | 1.83k | std::vector<std::pair<TxIdx, TxIdx>> active_dependencies; |
1654 | 49.1k | for (auto parent_idx : m_depgraph.Positions()) { Branch (1654:30): [True: 49.1k, False: 1.83k]
|
1655 | 51.3k | for (auto child_idx : m_depgraph.GetReducedChildren(parent_idx)) { Branch (1655:33): [True: 51.3k, False: 49.1k]
|
1656 | 51.3k | expected_dependencies.emplace_back(parent_idx, child_idx); |
1657 | 51.3k | } |
1658 | 49.1k | } |
1659 | 49.1k | for (auto tx_idx : m_transaction_idxs) { Branch (1659:26): [True: 49.1k, False: 1.83k]
|
1660 | 51.3k | for (auto child_idx : m_tx_data[tx_idx].children) { Branch (1660:33): [True: 51.3k, False: 49.1k]
|
1661 | 51.3k | all_dependencies.emplace_back(tx_idx, child_idx); |
1662 | 51.3k | if (m_tx_data[tx_idx].active_children[child_idx]) { Branch (1662:21): [True: 21.5k, False: 29.8k]
|
1663 | 21.5k | active_dependencies.emplace_back(tx_idx, child_idx); |
1664 | 21.5k | } |
1665 | 51.3k | } |
1666 | 49.1k | } |
1667 | 1.83k | std::ranges::sort(expected_dependencies); |
1668 | 1.83k | std::ranges::sort(all_dependencies); |
1669 | 1.83k | assert(expected_dependencies == all_dependencies); Branch (1669:9): [True: 1.83k, False: 0]
|
1670 | | |
1671 | | // |
1672 | | // Verify the chunks against the list of active dependencies |
1673 | | // |
1674 | 1.83k | SetType chunk_cover; |
1675 | 27.5k | for (auto chunk_idx : m_chunk_idxs) { Branch (1675:29): [True: 27.5k, False: 1.83k]
|
1676 | 27.5k | const auto& chunk_info = m_set_info[chunk_idx]; |
1677 | | // Verify that transactions in the chunk point back to it. This guarantees |
1678 | | // that chunks are non-overlapping. |
1679 | 49.1k | for (auto tx_idx : chunk_info.transactions) { Branch (1679:30): [True: 49.1k, False: 27.5k]
|
1680 | 49.1k | assert(m_tx_data[tx_idx].chunk_idx == chunk_idx); Branch (1680:17): [True: 49.1k, False: 0]
|
1681 | 49.1k | } |
1682 | 27.5k | assert(!chunk_cover.Overlaps(chunk_info.transactions)); Branch (1682:13): [True: 27.5k, False: 0]
|
1683 | 27.5k | chunk_cover |= chunk_info.transactions; |
1684 | | // Verify the chunk's transaction set: start from an arbitrary chunk transaction, |
1685 | | // and for every active dependency, if it contains the parent or child, add the |
1686 | | // other. It must have exactly N-1 active dependencies in it, guaranteeing it is |
1687 | | // acyclic. |
1688 | 27.5k | assert(chunk_info.transactions.Any()); Branch (1688:13): [True: 27.5k, False: 0]
|
1689 | 27.5k | SetType expected_chunk = SetType::Singleton(chunk_info.transactions.First()); |
1690 | 34.9k | while (true) { Branch (1690:20): [Folded - Ignored]
|
1691 | 34.9k | auto old = expected_chunk; |
1692 | 34.9k | size_t active_dep_count{0}; |
1693 | 330k | for (const auto& [par, chl] : active_dependencies) { Branch (1693:45): [True: 330k, False: 34.9k]
|
1694 | 330k | if (expected_chunk[par] || expected_chunk[chl]) { Branch (1694:25): [True: 59.5k, False: 270k]
Branch (1694:48): [True: 9.16k, False: 261k]
|
1695 | 68.7k | expected_chunk.Set(par); |
1696 | 68.7k | expected_chunk.Set(chl); |
1697 | 68.7k | ++active_dep_count; |
1698 | 68.7k | } |
1699 | 330k | } |
1700 | 34.9k | if (old == expected_chunk) { Branch (1700:21): [True: 27.5k, False: 7.33k]
|
1701 | 27.5k | assert(expected_chunk.Count() == active_dep_count + 1); Branch (1701:21): [True: 27.5k, False: 0]
|
1702 | 27.5k | break; |
1703 | 27.5k | } |
1704 | 34.9k | } |
1705 | 27.5k | assert(chunk_info.transactions == expected_chunk); Branch (1705:13): [True: 27.5k, False: 0]
|
1706 | | // Verify the chunk's feerate. |
1707 | 27.5k | assert(chunk_info.feerate == m_depgraph.FeeRate(chunk_info.transactions)); Branch (1707:13): [True: 27.5k, False: 0]
|
1708 | | // Verify the chunk's reachable transactions. |
1709 | 27.5k | assert(m_reachable[chunk_idx] == GetReachable(expected_chunk)); Branch (1709:13): [True: 27.5k, False: 0]
|
1710 | | // Verify that the chunk's reachable transactions don't include its own transactions. |
1711 | 27.5k | assert(!m_reachable[chunk_idx].first.Overlaps(chunk_info.transactions)); Branch (1711:13): [True: 27.5k, False: 0]
|
1712 | 27.5k | assert(!m_reachable[chunk_idx].second.Overlaps(chunk_info.transactions)); Branch (1712:13): [True: 27.5k, False: 0]
|
1713 | 27.5k | } |
1714 | | // Verify that together, the chunks cover all transactions. |
1715 | 1.83k | assert(chunk_cover == m_depgraph.Positions()); Branch (1715:9): [True: 1.83k, False: 0]
|
1716 | | |
1717 | | // |
1718 | | // Verify transaction data. |
1719 | | // |
1720 | 1.83k | assert(m_transaction_idxs == m_depgraph.Positions()); Branch (1720:9): [True: 1.83k, False: 0]
|
1721 | 49.1k | for (auto tx_idx : m_transaction_idxs) { Branch (1721:26): [True: 49.1k, False: 1.83k]
|
1722 | 49.1k | const auto& tx_data = m_tx_data[tx_idx]; |
1723 | | // Verify it has a valid chunk index, and that chunk includes this transaction. |
1724 | 49.1k | assert(m_chunk_idxs[tx_data.chunk_idx]); Branch (1724:13): [True: 49.1k, False: 0]
|
1725 | 49.1k | assert(m_set_info[tx_data.chunk_idx].transactions[tx_idx]); Branch (1725:13): [True: 49.1k, False: 0]
|
1726 | | // Verify parents/children. |
1727 | 49.1k | assert(tx_data.parents == m_depgraph.GetReducedParents(tx_idx)); Branch (1727:13): [True: 49.1k, False: 0]
|
1728 | 49.1k | assert(tx_data.children == m_depgraph.GetReducedChildren(tx_idx)); Branch (1728:13): [True: 49.1k, False: 0]
|
1729 | | // Verify active_children is a subset of children. |
1730 | 49.1k | assert(tx_data.active_children.IsSubsetOf(tx_data.children)); Branch (1730:13): [True: 49.1k, False: 0]
|
1731 | | // Verify each active child's dep_top_idx points to a valid non-chunk set. |
1732 | 49.1k | for (auto child_idx : tx_data.active_children) { Branch (1732:33): [True: 21.5k, False: 49.1k]
|
1733 | 21.5k | assert(tx_data.dep_top_idx[child_idx] < m_set_info.size()); Branch (1733:17): [True: 21.5k, False: 0]
|
1734 | 21.5k | assert(!m_chunk_idxs[tx_data.dep_top_idx[child_idx]]); Branch (1734:17): [True: 21.5k, False: 0]
|
1735 | 21.5k | } |
1736 | 49.1k | } |
1737 | | |
1738 | | // |
1739 | | // Verify active dependencies' top sets. |
1740 | | // |
1741 | 21.5k | for (const auto& [par_idx, chl_idx] : active_dependencies) { Branch (1741:45): [True: 21.5k, False: 1.83k]
|
1742 | | // Verify the top set's transactions: it must contain the parent, and for every |
1743 | | // active dependency, except the chl_idx->par_idx dependency itself, if it contains the |
1744 | | // parent or child, it must contain both. It must have exactly N-1 active dependencies |
1745 | | // in it, guaranteeing it is acyclic. |
1746 | 21.5k | SetType expected_top = SetType::Singleton(par_idx); |
1747 | 70.1k | while (true) { Branch (1747:20): [Folded - Ignored]
|
1748 | 70.1k | auto old = expected_top; |
1749 | 70.1k | size_t active_dep_count{0}; |
1750 | 1.51M | for (const auto& [par2_idx, chl2_idx] : active_dependencies) { Branch (1750:55): [True: 1.51M, False: 70.1k]
|
1751 | 1.51M | if (par_idx == par2_idx && chl_idx == chl2_idx) continue; Branch (1751:25): [True: 113k, False: 1.40M]
Branch (1751:48): [True: 70.1k, False: 43.3k]
|
1752 | 1.44M | if (expected_top[par2_idx] || expected_top[chl2_idx]) { Branch (1752:25): [True: 468k, False: 979k]
Branch (1752:51): [True: 62.4k, False: 916k]
|
1753 | 531k | expected_top.Set(par2_idx); |
1754 | 531k | expected_top.Set(chl2_idx); |
1755 | 531k | ++active_dep_count; |
1756 | 531k | } |
1757 | 1.44M | } |
1758 | 70.1k | if (old == expected_top) { Branch (1758:21): [True: 21.5k, False: 48.5k]
|
1759 | 21.5k | assert(expected_top.Count() == active_dep_count + 1); Branch (1759:21): [True: 21.5k, False: 0]
|
1760 | 21.5k | break; |
1761 | 21.5k | } |
1762 | 70.1k | } |
1763 | 21.5k | assert(!expected_top[chl_idx]); Branch (1763:13): [True: 21.5k, False: 0]
|
1764 | 21.5k | auto& dep_top_info = m_set_info[m_tx_data[par_idx].dep_top_idx[chl_idx]]; |
1765 | 21.5k | assert(dep_top_info.transactions == expected_top); Branch (1765:13): [True: 21.5k, False: 0]
|
1766 | | // Verify the top set's feerate. |
1767 | 21.5k | assert(dep_top_info.feerate == m_depgraph.FeeRate(dep_top_info.transactions)); Branch (1767:13): [True: 21.5k, False: 0]
|
1768 | 21.5k | } |
1769 | | |
1770 | | // |
1771 | | // Verify m_suboptimal_chunks. |
1772 | | // |
1773 | 1.83k | SetType suboptimal_idxs; |
1774 | 6.13k | for (size_t i = 0; i < m_suboptimal_chunks.size(); ++i) { Branch (1774:28): [True: 4.30k, False: 1.83k]
|
1775 | 4.30k | auto chunk_idx = m_suboptimal_chunks[i]; |
1776 | 4.30k | assert(!suboptimal_idxs[chunk_idx]); Branch (1776:13): [True: 4.30k, False: 0]
|
1777 | 4.30k | suboptimal_idxs.Set(chunk_idx); |
1778 | 4.30k | } |
1779 | 1.83k | assert(m_suboptimal_idxs == suboptimal_idxs); Branch (1779:9): [True: 1.83k, False: 0]
|
1780 | | |
1781 | | // |
1782 | | // Verify m_nonminimal_chunks. |
1783 | | // |
1784 | 1.83k | SetType nonminimal_idxs; |
1785 | 8.40k | for (size_t i = 0; i < m_nonminimal_chunks.size(); ++i) { Branch (1785:28): [True: 6.57k, False: 1.83k]
|
1786 | 6.57k | auto [chunk_idx, pivot, flags] = m_nonminimal_chunks[i]; |
1787 | 6.57k | assert(m_tx_data[pivot].chunk_idx == chunk_idx); Branch (1787:13): [True: 6.57k, False: 0]
|
1788 | 6.57k | assert(!nonminimal_idxs[chunk_idx]); Branch (1788:13): [True: 6.57k, False: 0]
|
1789 | 6.57k | nonminimal_idxs.Set(chunk_idx); |
1790 | 6.57k | } |
1791 | 1.83k | assert(nonminimal_idxs.IsSubsetOf(m_chunk_idxs)); Branch (1791:9): [True: 1.83k, False: 0]
|
1792 | 1.83k | } |
1793 | | }; |
1794 | | |
1795 | | /** Find or improve a linearization for a cluster. |
1796 | | * |
1797 | | * @param[in] depgraph Dependency graph of the cluster to be linearized. |
1798 | | * @param[in] max_cost Upper bound on the amount of work that will be done. |
1799 | | * @param[in] rng_seed A random number seed to control search order. This prevents peers |
1800 | | * from predicting exactly which clusters would be hard for us to |
1801 | | * linearize. |
1802 | | * @param[in] fallback_order A comparator to order transactions, used to sort equal-feerate |
1803 | | * chunks and transactions. See SpanningForestState::GetLinearization |
1804 | | * for details. |
1805 | | * @param[in] old_linearization An existing linearization for the cluster, or empty. |
1806 | | * @param[in] is_topological (Only relevant if old_linearization is not empty) Whether |
1807 | | * old_linearization is topologically valid. |
1808 | | * @return A tuple of: |
1809 | | * - The resulting linearization. It is guaranteed to be at least as |
1810 | | * good (in the feerate diagram sense) as old_linearization. |
1811 | | * - A boolean indicating whether the result is guaranteed to be |
1812 | | * optimal with minimal chunks. |
1813 | | * - How many optimization steps were actually performed. |
1814 | | */ |
1815 | | template<typename SetType> |
1816 | | std::tuple<std::vector<DepGraphIndex>, bool, uint64_t> Linearize( |
1817 | | const DepGraph<SetType>& depgraph, |
1818 | | uint64_t max_cost, |
1819 | | uint64_t rng_seed, |
1820 | | const StrongComparator<DepGraphIndex> auto& fallback_order, |
1821 | | std::span<const DepGraphIndex> old_linearization = {}, |
1822 | | bool is_topological = true) noexcept |
1823 | 1.32M | { |
1824 | | /** Initialize a spanning forest data structure for this cluster. */ |
1825 | 1.32M | SpanningForestState forest(depgraph, rng_seed); |
1826 | 1.32M | if (!old_linearization.empty()) { Branch (1826:9): [True: 480, False: 319]
Branch (1826:9): [True: 362, False: 27]
Branch (1826:9): [True: 1.32M, False: 0]
|
1827 | 1.32M | forest.LoadLinearization(old_linearization); |
1828 | 1.32M | if (!is_topological) forest.MakeTopological(); Branch (1828:13): [True: 53, False: 427]
Branch (1828:13): [True: 0, False: 362]
Branch (1828:13): [True: 772k, False: 547k]
|
1829 | 1.32M | } else { |
1830 | 346 | forest.MakeTopological(); |
1831 | 346 | } |
1832 | | // Make improvement steps to it until we hit the max_iterations limit, or an optimal result |
1833 | | // is found. |
1834 | 1.32M | if (forest.GetCost() < max_cost) { Branch (1834:9): [True: 741, False: 58]
Branch (1834:9): [True: 389, False: 0]
Branch (1834:9): [True: 1.30M, False: 18.5k]
|
1835 | 1.30M | forest.StartOptimizing(); |
1836 | 3.93M | do { |
1837 | 3.93M | if (!forest.OptimizeStep()) break; Branch (1837:17): [True: 738, False: 6.59k]
Branch (1837:17): [True: 389, False: 13.6k]
Branch (1837:17): [True: 1.30M, False: 2.61M]
|
1838 | 3.93M | } while (forest.GetCost() < max_cost); Branch (1838:18): [True: 6.58k, False: 3]
Branch (1838:18): [True: 13.6k, False: 0]
Branch (1838:18): [True: 2.61M, False: 849]
|
1839 | 1.30M | } |
1840 | | // Make chunk minimization steps until we hit the max_iterations limit, or all chunks are |
1841 | | // minimal. |
1842 | 0 | bool optimal = false; |
1843 | 1.32M | if (forest.GetCost() < max_cost) { Branch (1843:9): [True: 738, False: 61]
Branch (1843:9): [True: 389, False: 0]
Branch (1843:9): [True: 1.30M, False: 19.5k]
|
1844 | 1.30M | forest.StartMinimizing(); |
1845 | 6.65M | do { |
1846 | 6.65M | if (!forest.MinimizeStep()) { Branch (1846:17): [True: 719, False: 12.2k]
Branch (1846:17): [True: 389, False: 23.3k]
Branch (1846:17): [True: 1.29M, False: 5.31M]
|
1847 | 1.29M | optimal = true; |
1848 | 1.29M | break; |
1849 | 1.29M | } |
1850 | 6.65M | } while (forest.GetCost() < max_cost); Branch (1850:18): [True: 12.1k, False: 19]
Branch (1850:18): [True: 23.3k, False: 0]
Branch (1850:18): [True: 5.31M, False: 2.45k]
|
1851 | 1.30M | } |
1852 | 0 | return {forest.GetLinearization(fallback_order), optimal, forest.GetCost()}; |
1853 | 1.32M | } _ZN17cluster_linearize9LinearizeIN13bitset_detail9IntBitSetIjEETkNS_16StrongComparatorIjEESt17compare_three_wayEESt5tupleIJSt6vectorIjSaIjEEbmEERKNS_8DepGraphIT_EEmmRKT0_St4spanIKjLm18446744073709551615EEb Line | Count | Source | 1823 | 799 | { | 1824 | | /** Initialize a spanning forest data structure for this cluster. */ | 1825 | 799 | SpanningForestState forest(depgraph, rng_seed); | 1826 | 799 | if (!old_linearization.empty()) { Branch (1826:9): [True: 480, False: 319]
| 1827 | 480 | forest.LoadLinearization(old_linearization); | 1828 | 480 | if (!is_topological) forest.MakeTopological(); Branch (1828:13): [True: 53, False: 427]
| 1829 | 480 | } else { | 1830 | 319 | forest.MakeTopological(); | 1831 | 319 | } | 1832 | | // Make improvement steps to it until we hit the max_iterations limit, or an optimal result | 1833 | | // is found. | 1834 | 799 | if (forest.GetCost() < max_cost) { Branch (1834:9): [True: 741, False: 58]
| 1835 | 741 | forest.StartOptimizing(); | 1836 | 7.32k | do { | 1837 | 7.32k | if (!forest.OptimizeStep()) break; Branch (1837:17): [True: 738, False: 6.59k]
| 1838 | 7.32k | } while (forest.GetCost() < max_cost); Branch (1838:18): [True: 6.58k, False: 3]
| 1839 | 741 | } | 1840 | | // Make chunk minimization steps until we hit the max_iterations limit, or all chunks are | 1841 | | // minimal. | 1842 | 0 | bool optimal = false; | 1843 | 799 | if (forest.GetCost() < max_cost) { Branch (1843:9): [True: 738, False: 61]
| 1844 | 738 | forest.StartMinimizing(); | 1845 | 12.9k | do { | 1846 | 12.9k | if (!forest.MinimizeStep()) { Branch (1846:17): [True: 719, False: 12.2k]
| 1847 | 719 | optimal = true; | 1848 | 719 | break; | 1849 | 719 | } | 1850 | 12.9k | } while (forest.GetCost() < max_cost); Branch (1850:18): [True: 12.1k, False: 19]
| 1851 | 738 | } | 1852 | 0 | return {forest.GetLinearization(fallback_order), optimal, forest.GetCost()}; | 1853 | 799 | } |
txgraph.cpp:_ZN17cluster_linearize9LinearizeIN13bitset_detail14MultiIntBitSetImLj2EEETkNS_16StrongComparatorIjEEZ19txgraph_fuzz_targetSt4spanIKhLm18446744073709551615EEE3$_4EESt5tupleIJSt6vectorIjSaIjEEbmEERKNS_8DepGraphIT_EEmmRKT0_S5_IKjLm18446744073709551615EEb Line | Count | Source | 1823 | 389 | { | 1824 | | /** Initialize a spanning forest data structure for this cluster. */ | 1825 | 389 | SpanningForestState forest(depgraph, rng_seed); | 1826 | 389 | if (!old_linearization.empty()) { Branch (1826:9): [True: 362, False: 27]
| 1827 | 362 | forest.LoadLinearization(old_linearization); | 1828 | 362 | if (!is_topological) forest.MakeTopological(); Branch (1828:13): [True: 0, False: 362]
| 1829 | 362 | } else { | 1830 | 27 | forest.MakeTopological(); | 1831 | 27 | } | 1832 | | // Make improvement steps to it until we hit the max_iterations limit, or an optimal result | 1833 | | // is found. | 1834 | 389 | if (forest.GetCost() < max_cost) { Branch (1834:9): [True: 389, False: 0]
| 1835 | 389 | forest.StartOptimizing(); | 1836 | 14.0k | do { | 1837 | 14.0k | if (!forest.OptimizeStep()) break; Branch (1837:17): [True: 389, False: 13.6k]
| 1838 | 14.0k | } while (forest.GetCost() < max_cost); Branch (1838:18): [True: 13.6k, False: 0]
| 1839 | 389 | } | 1840 | | // Make chunk minimization steps until we hit the max_iterations limit, or all chunks are | 1841 | | // minimal. | 1842 | 0 | bool optimal = false; | 1843 | 389 | if (forest.GetCost() < max_cost) { Branch (1843:9): [True: 389, False: 0]
| 1844 | 389 | forest.StartMinimizing(); | 1845 | 23.7k | do { | 1846 | 23.7k | if (!forest.MinimizeStep()) { Branch (1846:17): [True: 389, False: 23.3k]
| 1847 | 389 | optimal = true; | 1848 | 389 | break; | 1849 | 389 | } | 1850 | 23.7k | } while (forest.GetCost() < max_cost); Branch (1850:18): [True: 23.3k, False: 0]
| 1851 | 389 | } | 1852 | 0 | return {forest.GetLinearization(fallback_order), optimal, forest.GetCost()}; | 1853 | 389 | } |
txgraph.cpp:_ZN17cluster_linearize9LinearizeIN13bitset_detail9IntBitSetImEETkNS_16StrongComparatorIjEEZN12_GLOBAL__N_118GenericClusterImpl11RelinearizeERNS5_11TxGraphImplEimE3$_0EESt5tupleIJSt6vectorIjSaIjEEbmEERKNS_8DepGraphIT_EEmmRKT0_St4spanIKjLm18446744073709551615EEb Line | Count | Source | 1823 | 1.32M | { | 1824 | | /** Initialize a spanning forest data structure for this cluster. */ | 1825 | 1.32M | SpanningForestState forest(depgraph, rng_seed); | 1826 | 1.32M | if (!old_linearization.empty()) { Branch (1826:9): [True: 1.32M, False: 0]
| 1827 | 1.32M | forest.LoadLinearization(old_linearization); | 1828 | 1.32M | if (!is_topological) forest.MakeTopological(); Branch (1828:13): [True: 772k, False: 547k]
| 1829 | 1.32M | } else { | 1830 | 0 | forest.MakeTopological(); | 1831 | 0 | } | 1832 | | // Make improvement steps to it until we hit the max_iterations limit, or an optimal result | 1833 | | // is found. | 1834 | 1.32M | if (forest.GetCost() < max_cost) { Branch (1834:9): [True: 1.30M, False: 18.5k]
| 1835 | 1.30M | forest.StartOptimizing(); | 1836 | 3.91M | do { | 1837 | 3.91M | if (!forest.OptimizeStep()) break; Branch (1837:17): [True: 1.30M, False: 2.61M]
| 1838 | 3.91M | } while (forest.GetCost() < max_cost); Branch (1838:18): [True: 2.61M, False: 849]
| 1839 | 1.30M | } | 1840 | | // Make chunk minimization steps until we hit the max_iterations limit, or all chunks are | 1841 | | // minimal. | 1842 | 0 | bool optimal = false; | 1843 | 1.32M | if (forest.GetCost() < max_cost) { Branch (1843:9): [True: 1.30M, False: 19.5k]
| 1844 | 1.30M | forest.StartMinimizing(); | 1845 | 6.61M | do { | 1846 | 6.61M | if (!forest.MinimizeStep()) { Branch (1846:17): [True: 1.29M, False: 5.31M]
| 1847 | 1.29M | optimal = true; | 1848 | 1.29M | break; | 1849 | 1.29M | } | 1850 | 6.61M | } while (forest.GetCost() < max_cost); Branch (1850:18): [True: 5.31M, False: 2.45k]
| 1851 | 1.30M | } | 1852 | 0 | return {forest.GetLinearization(fallback_order), optimal, forest.GetCost()}; | 1853 | 1.32M | } |
|
1854 | | |
1855 | | /** Improve a given linearization. |
1856 | | * |
1857 | | * @param[in] depgraph Dependency graph of the cluster being linearized. |
1858 | | * @param[in,out] linearization On input, an existing linearization for depgraph. On output, a |
1859 | | * potentially better linearization for the same graph. |
1860 | | * |
1861 | | * Postlinearization guarantees: |
1862 | | * - The resulting chunks are connected. |
1863 | | * - If the input has a tree shape (either all transactions have at most one child, or all |
1864 | | * transactions have at most one parent), the result is optimal. |
1865 | | * - Given a linearization L1 and a leaf transaction T in it. Let L2 be L1 with T moved to the end, |
1866 | | * optionally with its fee increased. Let L3 be the postlinearization of L2. L3 will be at least |
1867 | | * as good as L1. This means that replacing transactions with same-size higher-fee transactions |
1868 | | * will not worsen linearizations through a "drop conflicts, append new transactions, |
1869 | | * postlinearize" process. |
1870 | | */ |
1871 | | template<typename SetType> |
1872 | | void PostLinearize(const DepGraph<SetType>& depgraph, std::span<DepGraphIndex> linearization) |
1873 | 1.32M | { |
1874 | | // This algorithm performs a number of passes (currently 2); the even ones operate from back to |
1875 | | // front, the odd ones from front to back. Each results in an equal-or-better linearization |
1876 | | // than the one started from. |
1877 | | // - One pass in either direction guarantees that the resulting chunks are connected. |
1878 | | // - Each direction corresponds to one shape of tree being linearized optimally (forward passes |
1879 | | // guarantee this for graphs where each transaction has at most one child; backward passes |
1880 | | // guarantee this for graphs where each transaction has at most one parent). |
1881 | | // - Starting with a backward pass guarantees the moved-tree property. |
1882 | | // |
1883 | | // During an odd (forward) pass, the high-level operation is: |
1884 | | // - Start with an empty list of groups L=[]. |
1885 | | // - For every transaction i in the old linearization, from front to back: |
1886 | | // - Append a new group C=[i], containing just i, to the back of L. |
1887 | | // - While L has at least one group before C, and the group immediately before C has feerate |
1888 | | // lower than C: |
1889 | | // - If C depends on P: |
1890 | | // - Merge P into C, making C the concatenation of P+C, continuing with the combined C. |
1891 | | // - Otherwise: |
1892 | | // - Swap P with C, continuing with the now-moved C. |
1893 | | // - The output linearization is the concatenation of the groups in L. |
1894 | | // |
1895 | | // During even (backward) passes, i iterates from the back to the front of the existing |
1896 | | // linearization, and new groups are prepended instead of appended to the list L. To enable |
1897 | | // more code reuse, both passes append groups, but during even passes the meanings of |
1898 | | // parent/child, and of high/low feerate are reversed, and the final concatenation is reversed |
1899 | | // on output. |
1900 | | // |
1901 | | // In the implementation below, the groups are represented by singly-linked lists (pointing |
1902 | | // from the back to the front), which are themselves organized in a singly-linked circular |
1903 | | // list (each group pointing to its predecessor, with a special sentinel group at the front |
1904 | | // that points back to the last group). |
1905 | | // |
1906 | | // Information about transaction t is stored in entries[t + 1], while the sentinel is in |
1907 | | // entries[0]. |
1908 | | |
1909 | | /** Index of the sentinel in the entries array below. */ |
1910 | 1.32M | static constexpr DepGraphIndex SENTINEL{0}; |
1911 | | /** Indicator that a group has no previous transaction. */ |
1912 | 1.32M | static constexpr DepGraphIndex NO_PREV_TX{0}; |
1913 | | |
1914 | | |
1915 | | /** Data structure per transaction entry. */ |
1916 | 1.32M | struct TxEntry |
1917 | 1.32M | { |
1918 | | /** The index of the previous transaction in this group; NO_PREV_TX if this is the first |
1919 | | * entry of a group. */ |
1920 | 1.32M | DepGraphIndex prev_tx; |
1921 | | |
1922 | | // The fields below are only used for transactions that are the last one in a group |
1923 | | // (referred to as tail transactions below). |
1924 | | |
1925 | | /** Index of the first transaction in this group, possibly itself. */ |
1926 | 1.32M | DepGraphIndex first_tx; |
1927 | | /** Index of the last transaction in the previous group. The first group (the sentinel) |
1928 | | * points back to the last group here, making it a singly-linked circular list. */ |
1929 | 1.32M | DepGraphIndex prev_group; |
1930 | | /** All transactions in the group. Empty for the sentinel. */ |
1931 | 1.32M | SetType group; |
1932 | | /** All dependencies of the group (descendants in even passes; ancestors in odd ones). */ |
1933 | 1.32M | SetType deps; |
1934 | | /** The combined fee/size of transactions in the group. Fee is negated in even passes. */ |
1935 | 1.32M | FeeFrac feerate; |
1936 | 1.32M | }; |
1937 | | |
1938 | | // As an example, consider the state corresponding to the linearization [1,0,3,2], with |
1939 | | // groups [1,0,3] and [2], in an odd pass. The linked lists would be: |
1940 | | // |
1941 | | // +-----+ |
1942 | | // 0<-P-- | 0 S | ---\ Legend: |
1943 | | // +-----+ | |
1944 | | // ^ | - digit in box: entries index |
1945 | | // /--------------F---------+ G | (note: one more than tx value) |
1946 | | // v \ | | - S: sentinel group |
1947 | | // +-----+ +-----+ +-----+ | (empty feerate) |
1948 | | // 0<-P-- | 2 | <--P-- | 1 | <--P-- | 4 T | | - T: tail transaction, contains |
1949 | | // +-----+ +-----+ +-----+ | fields beyond prev_tv. |
1950 | | // ^ | - P: prev_tx reference |
1951 | | // G G - F: first_tx reference |
1952 | | // | | - G: prev_group reference |
1953 | | // +-----+ | |
1954 | | // 0<-P-- | 3 T | <--/ |
1955 | | // +-----+ |
1956 | | // ^ | |
1957 | | // \-F-/ |
1958 | | // |
1959 | | // During an even pass, the diagram above would correspond to linearization [2,3,0,1], with |
1960 | | // groups [2] and [3,0,1]. |
1961 | | |
1962 | 1.32M | std::vector<TxEntry> entries(depgraph.PositionRange() + 1); |
1963 | | |
1964 | | // Perform two passes over the linearization. |
1965 | 3.96M | for (int pass = 0; pass < 2; ++pass) { Branch (1965:24): [True: 1.35k, False: 678]
Branch (1965:24): [True: 778, False: 389]
Branch (1965:24): [True: 2.64M, False: 1.32M]
|
1966 | 2.64M | int rev = !(pass & 1); |
1967 | | // Construct a sentinel group, identifying the start of the list. |
1968 | 2.64M | entries[SENTINEL].prev_group = SENTINEL; |
1969 | 2.64M | Assume(entries[SENTINEL].feerate.IsEmpty()); |
1970 | | |
1971 | | // Iterate over all elements in the existing linearization. |
1972 | 16.0M | for (DepGraphIndex i = 0; i < linearization.size(); ++i) { Branch (1972:35): [True: 22.2k, False: 1.35k]
Branch (1972:35): [True: 42.3k, False: 778]
Branch (1972:35): [True: 13.3M, False: 2.64M]
|
1973 | | // Even passes are from back to front; odd passes from front to back. |
1974 | 13.3M | DepGraphIndex idx = linearization[rev ? linearization.size() - 1 - i : i]; Branch (1974:47): [True: 11.1k, False: 11.1k]
Branch (1974:47): [True: 21.1k, False: 21.1k]
Branch (1974:47): [True: 6.66M, False: 6.66M]
|
1975 | | // Construct a new group containing just idx. In even passes, the meaning of |
1976 | | // parent/child and high/low feerate are swapped. |
1977 | 13.3M | DepGraphIndex cur_group = idx + 1; |
1978 | 13.3M | entries[cur_group].group = SetType::Singleton(idx); |
1979 | 13.3M | entries[cur_group].deps = rev ? depgraph.Descendants(idx): depgraph.Ancestors(idx); Branch (1979:39): [True: 11.1k, False: 11.1k]
Branch (1979:39): [True: 21.1k, False: 21.1k]
Branch (1979:39): [True: 6.66M, False: 6.66M]
|
1980 | 13.3M | entries[cur_group].feerate = depgraph.FeeRate(idx); |
1981 | 13.3M | if (rev) entries[cur_group].feerate.fee = -entries[cur_group].feerate.fee; Branch (1981:17): [True: 11.1k, False: 11.1k]
Branch (1981:17): [True: 21.1k, False: 21.1k]
Branch (1981:17): [True: 6.66M, False: 6.66M]
|
1982 | 13.3M | entries[cur_group].prev_tx = NO_PREV_TX; // No previous transaction in group. |
1983 | 13.3M | entries[cur_group].first_tx = cur_group; // Transaction itself is first of group. |
1984 | | // Insert the new group at the back of the groups linked list. |
1985 | 13.3M | entries[cur_group].prev_group = entries[SENTINEL].prev_group; |
1986 | 13.3M | entries[SENTINEL].prev_group = cur_group; |
1987 | | |
1988 | | // Start merge/swap cycle. |
1989 | 13.3M | DepGraphIndex next_group = SENTINEL; // We inserted at the end, so next group is sentinel. |
1990 | 13.3M | DepGraphIndex prev_group = entries[cur_group].prev_group; |
1991 | | // Continue as long as the current group has higher feerate than the previous one. |
1992 | 17.7M | while (ByRatio{entries[cur_group].feerate} > ByRatio{entries[prev_group].feerate}) { Branch (1992:20): [True: 19.4k, False: 22.2k]
Branch (1992:20): [True: 7.36k, False: 42.3k]
Branch (1992:20): [True: 4.30M, False: 13.3M]
|
1993 | | // prev_group/cur_group/next_group refer to (the last transactions of) 3 |
1994 | | // consecutive entries in groups list. |
1995 | 4.33M | Assume(cur_group == entries[next_group].prev_group); |
1996 | 4.33M | Assume(prev_group == entries[cur_group].prev_group); |
1997 | | // The sentinel has empty feerate, which is neither higher or lower than other |
1998 | | // feerates. Thus, the while loop we are in here guarantees that cur_group and |
1999 | | // prev_group are not the sentinel. |
2000 | 4.33M | Assume(cur_group != SENTINEL); |
2001 | 4.33M | Assume(prev_group != SENTINEL); |
2002 | 4.33M | if (entries[cur_group].deps.Overlaps(entries[prev_group].group)) { Branch (2002:21): [True: 9.12k, False: 10.3k]
Branch (2002:21): [True: 6.67k, False: 685]
Branch (2002:21): [True: 4.08M, False: 227k]
|
2003 | | // There is a dependency between cur_group and prev_group; merge prev_group |
2004 | | // into cur_group. The group/deps/feerate fields of prev_group remain unchanged |
2005 | | // but become unused. |
2006 | 4.09M | entries[cur_group].group |= entries[prev_group].group; |
2007 | 4.09M | entries[cur_group].deps |= entries[prev_group].deps; |
2008 | 4.09M | entries[cur_group].feerate += entries[prev_group].feerate; |
2009 | | // Make the first of the current group point to the tail of the previous group. |
2010 | 4.09M | entries[entries[cur_group].first_tx].prev_tx = prev_group; |
2011 | | // The first of the previous group becomes the first of the newly-merged group. |
2012 | 4.09M | entries[cur_group].first_tx = entries[prev_group].first_tx; |
2013 | | // The previous group becomes whatever group was before the former one. |
2014 | 4.09M | prev_group = entries[prev_group].prev_group; |
2015 | 4.09M | entries[cur_group].prev_group = prev_group; |
2016 | 4.09M | } else { |
2017 | | // There is no dependency between cur_group and prev_group; swap them. |
2018 | 238k | DepGraphIndex preprev_group = entries[prev_group].prev_group; |
2019 | | // If PP, P, C, N were the old preprev, prev, cur, next groups, then the new |
2020 | | // layout becomes [PP, C, P, N]. Update prev_groups to reflect that order. |
2021 | 238k | entries[next_group].prev_group = prev_group; |
2022 | 238k | entries[prev_group].prev_group = cur_group; |
2023 | 238k | entries[cur_group].prev_group = preprev_group; |
2024 | | // The current group remains the same, but the groups before/after it have |
2025 | | // changed. |
2026 | 238k | next_group = prev_group; |
2027 | 238k | prev_group = preprev_group; |
2028 | 238k | } |
2029 | 4.33M | } |
2030 | 13.3M | } |
2031 | | |
2032 | | // Convert the entries back to linearization (overwriting the existing one). |
2033 | 2.64M | DepGraphIndex cur_group = entries[0].prev_group; |
2034 | 2.64M | DepGraphIndex done = 0; |
2035 | 11.9M | while (cur_group != SENTINEL) { Branch (2035:16): [True: 13.1k, False: 1.35k]
Branch (2035:16): [True: 35.6k, False: 778]
Branch (2035:16): [True: 9.24M, False: 2.64M]
|
2036 | 9.29M | DepGraphIndex cur_tx = cur_group; |
2037 | | // Traverse the transactions of cur_group (from back to front), and write them in the |
2038 | | // same order during odd passes, and reversed (front to back) in even passes. |
2039 | 9.29M | if (rev) { Branch (2039:17): [True: 6.51k, False: 6.58k]
Branch (2039:17): [True: 17.8k, False: 17.8k]
Branch (2039:17): [True: 4.62M, False: 4.62M]
|
2040 | 6.69M | do { |
2041 | 6.69M | *(linearization.begin() + (done++)) = cur_tx - 1; |
2042 | 6.69M | cur_tx = entries[cur_tx].prev_tx; |
2043 | 6.69M | } while (cur_tx != NO_PREV_TX); Branch (2043:26): [True: 4.59k, False: 6.51k]
Branch (2043:26): [True: 3.33k, False: 17.8k]
Branch (2043:26): [True: 2.04M, False: 4.62M]
|
2044 | 4.64M | } else { |
2045 | 6.69M | do { |
2046 | 6.69M | *(linearization.end() - (++done)) = cur_tx - 1; |
2047 | 6.69M | cur_tx = entries[cur_tx].prev_tx; |
2048 | 6.69M | } while (cur_tx != NO_PREV_TX); Branch (2048:26): [True: 4.52k, False: 6.58k]
Branch (2048:26): [True: 3.33k, False: 17.8k]
Branch (2048:26): [True: 2.04M, False: 4.62M]
|
2049 | 4.64M | } |
2050 | 9.29M | cur_group = entries[cur_group].prev_group; |
2051 | 9.29M | } |
2052 | 2.64M | Assume(done == linearization.size()); |
2053 | 2.64M | } |
2054 | 1.32M | } _ZN17cluster_linearize13PostLinearizeIN13bitset_detail9IntBitSetIjEEEEvRKNS_8DepGraphIT_EESt4spanIjLm18446744073709551615EE Line | Count | Source | 1873 | 678 | { | 1874 | | // This algorithm performs a number of passes (currently 2); the even ones operate from back to | 1875 | | // front, the odd ones from front to back. Each results in an equal-or-better linearization | 1876 | | // than the one started from. | 1877 | | // - One pass in either direction guarantees that the resulting chunks are connected. | 1878 | | // - Each direction corresponds to one shape of tree being linearized optimally (forward passes | 1879 | | // guarantee this for graphs where each transaction has at most one child; backward passes | 1880 | | // guarantee this for graphs where each transaction has at most one parent). | 1881 | | // - Starting with a backward pass guarantees the moved-tree property. | 1882 | | // | 1883 | | // During an odd (forward) pass, the high-level operation is: | 1884 | | // - Start with an empty list of groups L=[]. | 1885 | | // - For every transaction i in the old linearization, from front to back: | 1886 | | // - Append a new group C=[i], containing just i, to the back of L. | 1887 | | // - While L has at least one group before C, and the group immediately before C has feerate | 1888 | | // lower than C: | 1889 | | // - If C depends on P: | 1890 | | // - Merge P into C, making C the concatenation of P+C, continuing with the combined C. | 1891 | | // - Otherwise: | 1892 | | // - Swap P with C, continuing with the now-moved C. | 1893 | | // - The output linearization is the concatenation of the groups in L. | 1894 | | // | 1895 | | // During even (backward) passes, i iterates from the back to the front of the existing | 1896 | | // linearization, and new groups are prepended instead of appended to the list L. To enable | 1897 | | // more code reuse, both passes append groups, but during even passes the meanings of | 1898 | | // parent/child, and of high/low feerate are reversed, and the final concatenation is reversed | 1899 | | // on output. | 1900 | | // | 1901 | | // In the implementation below, the groups are represented by singly-linked lists (pointing | 1902 | | // from the back to the front), which are themselves organized in a singly-linked circular | 1903 | | // list (each group pointing to its predecessor, with a special sentinel group at the front | 1904 | | // that points back to the last group). | 1905 | | // | 1906 | | // Information about transaction t is stored in entries[t + 1], while the sentinel is in | 1907 | | // entries[0]. | 1908 | | | 1909 | | /** Index of the sentinel in the entries array below. */ | 1910 | 678 | static constexpr DepGraphIndex SENTINEL{0}; | 1911 | | /** Indicator that a group has no previous transaction. */ | 1912 | 678 | static constexpr DepGraphIndex NO_PREV_TX{0}; | 1913 | | | 1914 | | | 1915 | | /** Data structure per transaction entry. */ | 1916 | 678 | struct TxEntry | 1917 | 678 | { | 1918 | | /** The index of the previous transaction in this group; NO_PREV_TX if this is the first | 1919 | | * entry of a group. */ | 1920 | 678 | DepGraphIndex prev_tx; | 1921 | | | 1922 | | // The fields below are only used for transactions that are the last one in a group | 1923 | | // (referred to as tail transactions below). | 1924 | | | 1925 | | /** Index of the first transaction in this group, possibly itself. */ | 1926 | 678 | DepGraphIndex first_tx; | 1927 | | /** Index of the last transaction in the previous group. The first group (the sentinel) | 1928 | | * points back to the last group here, making it a singly-linked circular list. */ | 1929 | 678 | DepGraphIndex prev_group; | 1930 | | /** All transactions in the group. Empty for the sentinel. */ | 1931 | 678 | SetType group; | 1932 | | /** All dependencies of the group (descendants in even passes; ancestors in odd ones). */ | 1933 | 678 | SetType deps; | 1934 | | /** The combined fee/size of transactions in the group. Fee is negated in even passes. */ | 1935 | 678 | FeeFrac feerate; | 1936 | 678 | }; | 1937 | | | 1938 | | // As an example, consider the state corresponding to the linearization [1,0,3,2], with | 1939 | | // groups [1,0,3] and [2], in an odd pass. The linked lists would be: | 1940 | | // | 1941 | | // +-----+ | 1942 | | // 0<-P-- | 0 S | ---\ Legend: | 1943 | | // +-----+ | | 1944 | | // ^ | - digit in box: entries index | 1945 | | // /--------------F---------+ G | (note: one more than tx value) | 1946 | | // v \ | | - S: sentinel group | 1947 | | // +-----+ +-----+ +-----+ | (empty feerate) | 1948 | | // 0<-P-- | 2 | <--P-- | 1 | <--P-- | 4 T | | - T: tail transaction, contains | 1949 | | // +-----+ +-----+ +-----+ | fields beyond prev_tv. | 1950 | | // ^ | - P: prev_tx reference | 1951 | | // G G - F: first_tx reference | 1952 | | // | | - G: prev_group reference | 1953 | | // +-----+ | | 1954 | | // 0<-P-- | 3 T | <--/ | 1955 | | // +-----+ | 1956 | | // ^ | | 1957 | | // \-F-/ | 1958 | | // | 1959 | | // During an even pass, the diagram above would correspond to linearization [2,3,0,1], with | 1960 | | // groups [2] and [3,0,1]. | 1961 | | | 1962 | 678 | std::vector<TxEntry> entries(depgraph.PositionRange() + 1); | 1963 | | | 1964 | | // Perform two passes over the linearization. | 1965 | 2.03k | for (int pass = 0; pass < 2; ++pass) { Branch (1965:24): [True: 1.35k, False: 678]
| 1966 | 1.35k | int rev = !(pass & 1); | 1967 | | // Construct a sentinel group, identifying the start of the list. | 1968 | 1.35k | entries[SENTINEL].prev_group = SENTINEL; | 1969 | 1.35k | Assume(entries[SENTINEL].feerate.IsEmpty()); | 1970 | | | 1971 | | // Iterate over all elements in the existing linearization. | 1972 | 23.5k | for (DepGraphIndex i = 0; i < linearization.size(); ++i) { Branch (1972:35): [True: 22.2k, False: 1.35k]
| 1973 | | // Even passes are from back to front; odd passes from front to back. | 1974 | 22.2k | DepGraphIndex idx = linearization[rev ? linearization.size() - 1 - i : i]; Branch (1974:47): [True: 11.1k, False: 11.1k]
| 1975 | | // Construct a new group containing just idx. In even passes, the meaning of | 1976 | | // parent/child and high/low feerate are swapped. | 1977 | 22.2k | DepGraphIndex cur_group = idx + 1; | 1978 | 22.2k | entries[cur_group].group = SetType::Singleton(idx); | 1979 | 22.2k | entries[cur_group].deps = rev ? depgraph.Descendants(idx): depgraph.Ancestors(idx); Branch (1979:39): [True: 11.1k, False: 11.1k]
| 1980 | 22.2k | entries[cur_group].feerate = depgraph.FeeRate(idx); | 1981 | 22.2k | if (rev) entries[cur_group].feerate.fee = -entries[cur_group].feerate.fee; Branch (1981:17): [True: 11.1k, False: 11.1k]
| 1982 | 22.2k | entries[cur_group].prev_tx = NO_PREV_TX; // No previous transaction in group. | 1983 | 22.2k | entries[cur_group].first_tx = cur_group; // Transaction itself is first of group. | 1984 | | // Insert the new group at the back of the groups linked list. | 1985 | 22.2k | entries[cur_group].prev_group = entries[SENTINEL].prev_group; | 1986 | 22.2k | entries[SENTINEL].prev_group = cur_group; | 1987 | | | 1988 | | // Start merge/swap cycle. | 1989 | 22.2k | DepGraphIndex next_group = SENTINEL; // We inserted at the end, so next group is sentinel. | 1990 | 22.2k | DepGraphIndex prev_group = entries[cur_group].prev_group; | 1991 | | // Continue as long as the current group has higher feerate than the previous one. | 1992 | 41.6k | while (ByRatio{entries[cur_group].feerate} > ByRatio{entries[prev_group].feerate}) { Branch (1992:20): [True: 19.4k, False: 22.2k]
| 1993 | | // prev_group/cur_group/next_group refer to (the last transactions of) 3 | 1994 | | // consecutive entries in groups list. | 1995 | 19.4k | Assume(cur_group == entries[next_group].prev_group); | 1996 | 19.4k | Assume(prev_group == entries[cur_group].prev_group); | 1997 | | // The sentinel has empty feerate, which is neither higher or lower than other | 1998 | | // feerates. Thus, the while loop we are in here guarantees that cur_group and | 1999 | | // prev_group are not the sentinel. | 2000 | 19.4k | Assume(cur_group != SENTINEL); | 2001 | 19.4k | Assume(prev_group != SENTINEL); | 2002 | 19.4k | if (entries[cur_group].deps.Overlaps(entries[prev_group].group)) { Branch (2002:21): [True: 9.12k, False: 10.3k]
| 2003 | | // There is a dependency between cur_group and prev_group; merge prev_group | 2004 | | // into cur_group. The group/deps/feerate fields of prev_group remain unchanged | 2005 | | // but become unused. | 2006 | 9.12k | entries[cur_group].group |= entries[prev_group].group; | 2007 | 9.12k | entries[cur_group].deps |= entries[prev_group].deps; | 2008 | 9.12k | entries[cur_group].feerate += entries[prev_group].feerate; | 2009 | | // Make the first of the current group point to the tail of the previous group. | 2010 | 9.12k | entries[entries[cur_group].first_tx].prev_tx = prev_group; | 2011 | | // The first of the previous group becomes the first of the newly-merged group. | 2012 | 9.12k | entries[cur_group].first_tx = entries[prev_group].first_tx; | 2013 | | // The previous group becomes whatever group was before the former one. | 2014 | 9.12k | prev_group = entries[prev_group].prev_group; | 2015 | 9.12k | entries[cur_group].prev_group = prev_group; | 2016 | 10.3k | } else { | 2017 | | // There is no dependency between cur_group and prev_group; swap them. | 2018 | 10.3k | DepGraphIndex preprev_group = entries[prev_group].prev_group; | 2019 | | // If PP, P, C, N were the old preprev, prev, cur, next groups, then the new | 2020 | | // layout becomes [PP, C, P, N]. Update prev_groups to reflect that order. | 2021 | 10.3k | entries[next_group].prev_group = prev_group; | 2022 | 10.3k | entries[prev_group].prev_group = cur_group; | 2023 | 10.3k | entries[cur_group].prev_group = preprev_group; | 2024 | | // The current group remains the same, but the groups before/after it have | 2025 | | // changed. | 2026 | 10.3k | next_group = prev_group; | 2027 | 10.3k | prev_group = preprev_group; | 2028 | 10.3k | } | 2029 | 19.4k | } | 2030 | 22.2k | } | 2031 | | | 2032 | | // Convert the entries back to linearization (overwriting the existing one). | 2033 | 1.35k | DepGraphIndex cur_group = entries[0].prev_group; | 2034 | 1.35k | DepGraphIndex done = 0; | 2035 | 14.4k | while (cur_group != SENTINEL) { Branch (2035:16): [True: 13.1k, False: 1.35k]
| 2036 | 13.1k | DepGraphIndex cur_tx = cur_group; | 2037 | | // Traverse the transactions of cur_group (from back to front), and write them in the | 2038 | | // same order during odd passes, and reversed (front to back) in even passes. | 2039 | 13.1k | if (rev) { Branch (2039:17): [True: 6.51k, False: 6.58k]
| 2040 | 11.1k | do { | 2041 | 11.1k | *(linearization.begin() + (done++)) = cur_tx - 1; | 2042 | 11.1k | cur_tx = entries[cur_tx].prev_tx; | 2043 | 11.1k | } while (cur_tx != NO_PREV_TX); Branch (2043:26): [True: 4.59k, False: 6.51k]
| 2044 | 6.58k | } else { | 2045 | 11.1k | do { | 2046 | 11.1k | *(linearization.end() - (++done)) = cur_tx - 1; | 2047 | 11.1k | cur_tx = entries[cur_tx].prev_tx; | 2048 | 11.1k | } while (cur_tx != NO_PREV_TX); Branch (2048:26): [True: 4.52k, False: 6.58k]
| 2049 | 6.58k | } | 2050 | 13.1k | cur_group = entries[cur_group].prev_group; | 2051 | 13.1k | } | 2052 | 1.35k | Assume(done == linearization.size()); | 2053 | 1.35k | } | 2054 | 678 | } |
_ZN17cluster_linearize13PostLinearizeIN13bitset_detail14MultiIntBitSetImLj2EEEEEvRKNS_8DepGraphIT_EESt4spanIjLm18446744073709551615EE Line | Count | Source | 1873 | 389 | { | 1874 | | // This algorithm performs a number of passes (currently 2); the even ones operate from back to | 1875 | | // front, the odd ones from front to back. Each results in an equal-or-better linearization | 1876 | | // than the one started from. | 1877 | | // - One pass in either direction guarantees that the resulting chunks are connected. | 1878 | | // - Each direction corresponds to one shape of tree being linearized optimally (forward passes | 1879 | | // guarantee this for graphs where each transaction has at most one child; backward passes | 1880 | | // guarantee this for graphs where each transaction has at most one parent). | 1881 | | // - Starting with a backward pass guarantees the moved-tree property. | 1882 | | // | 1883 | | // During an odd (forward) pass, the high-level operation is: | 1884 | | // - Start with an empty list of groups L=[]. | 1885 | | // - For every transaction i in the old linearization, from front to back: | 1886 | | // - Append a new group C=[i], containing just i, to the back of L. | 1887 | | // - While L has at least one group before C, and the group immediately before C has feerate | 1888 | | // lower than C: | 1889 | | // - If C depends on P: | 1890 | | // - Merge P into C, making C the concatenation of P+C, continuing with the combined C. | 1891 | | // - Otherwise: | 1892 | | // - Swap P with C, continuing with the now-moved C. | 1893 | | // - The output linearization is the concatenation of the groups in L. | 1894 | | // | 1895 | | // During even (backward) passes, i iterates from the back to the front of the existing | 1896 | | // linearization, and new groups are prepended instead of appended to the list L. To enable | 1897 | | // more code reuse, both passes append groups, but during even passes the meanings of | 1898 | | // parent/child, and of high/low feerate are reversed, and the final concatenation is reversed | 1899 | | // on output. | 1900 | | // | 1901 | | // In the implementation below, the groups are represented by singly-linked lists (pointing | 1902 | | // from the back to the front), which are themselves organized in a singly-linked circular | 1903 | | // list (each group pointing to its predecessor, with a special sentinel group at the front | 1904 | | // that points back to the last group). | 1905 | | // | 1906 | | // Information about transaction t is stored in entries[t + 1], while the sentinel is in | 1907 | | // entries[0]. | 1908 | | | 1909 | | /** Index of the sentinel in the entries array below. */ | 1910 | 389 | static constexpr DepGraphIndex SENTINEL{0}; | 1911 | | /** Indicator that a group has no previous transaction. */ | 1912 | 389 | static constexpr DepGraphIndex NO_PREV_TX{0}; | 1913 | | | 1914 | | | 1915 | | /** Data structure per transaction entry. */ | 1916 | 389 | struct TxEntry | 1917 | 389 | { | 1918 | | /** The index of the previous transaction in this group; NO_PREV_TX if this is the first | 1919 | | * entry of a group. */ | 1920 | 389 | DepGraphIndex prev_tx; | 1921 | | | 1922 | | // The fields below are only used for transactions that are the last one in a group | 1923 | | // (referred to as tail transactions below). | 1924 | | | 1925 | | /** Index of the first transaction in this group, possibly itself. */ | 1926 | 389 | DepGraphIndex first_tx; | 1927 | | /** Index of the last transaction in the previous group. The first group (the sentinel) | 1928 | | * points back to the last group here, making it a singly-linked circular list. */ | 1929 | 389 | DepGraphIndex prev_group; | 1930 | | /** All transactions in the group. Empty for the sentinel. */ | 1931 | 389 | SetType group; | 1932 | | /** All dependencies of the group (descendants in even passes; ancestors in odd ones). */ | 1933 | 389 | SetType deps; | 1934 | | /** The combined fee/size of transactions in the group. Fee is negated in even passes. */ | 1935 | 389 | FeeFrac feerate; | 1936 | 389 | }; | 1937 | | | 1938 | | // As an example, consider the state corresponding to the linearization [1,0,3,2], with | 1939 | | // groups [1,0,3] and [2], in an odd pass. The linked lists would be: | 1940 | | // | 1941 | | // +-----+ | 1942 | | // 0<-P-- | 0 S | ---\ Legend: | 1943 | | // +-----+ | | 1944 | | // ^ | - digit in box: entries index | 1945 | | // /--------------F---------+ G | (note: one more than tx value) | 1946 | | // v \ | | - S: sentinel group | 1947 | | // +-----+ +-----+ +-----+ | (empty feerate) | 1948 | | // 0<-P-- | 2 | <--P-- | 1 | <--P-- | 4 T | | - T: tail transaction, contains | 1949 | | // +-----+ +-----+ +-----+ | fields beyond prev_tv. | 1950 | | // ^ | - P: prev_tx reference | 1951 | | // G G - F: first_tx reference | 1952 | | // | | - G: prev_group reference | 1953 | | // +-----+ | | 1954 | | // 0<-P-- | 3 T | <--/ | 1955 | | // +-----+ | 1956 | | // ^ | | 1957 | | // \-F-/ | 1958 | | // | 1959 | | // During an even pass, the diagram above would correspond to linearization [2,3,0,1], with | 1960 | | // groups [2] and [3,0,1]. | 1961 | | | 1962 | 389 | std::vector<TxEntry> entries(depgraph.PositionRange() + 1); | 1963 | | | 1964 | | // Perform two passes over the linearization. | 1965 | 1.16k | for (int pass = 0; pass < 2; ++pass) { Branch (1965:24): [True: 778, False: 389]
| 1966 | 778 | int rev = !(pass & 1); | 1967 | | // Construct a sentinel group, identifying the start of the list. | 1968 | 778 | entries[SENTINEL].prev_group = SENTINEL; | 1969 | 778 | Assume(entries[SENTINEL].feerate.IsEmpty()); | 1970 | | | 1971 | | // Iterate over all elements in the existing linearization. | 1972 | 43.0k | for (DepGraphIndex i = 0; i < linearization.size(); ++i) { Branch (1972:35): [True: 42.3k, False: 778]
| 1973 | | // Even passes are from back to front; odd passes from front to back. | 1974 | 42.3k | DepGraphIndex idx = linearization[rev ? linearization.size() - 1 - i : i]; Branch (1974:47): [True: 21.1k, False: 21.1k]
| 1975 | | // Construct a new group containing just idx. In even passes, the meaning of | 1976 | | // parent/child and high/low feerate are swapped. | 1977 | 42.3k | DepGraphIndex cur_group = idx + 1; | 1978 | 42.3k | entries[cur_group].group = SetType::Singleton(idx); | 1979 | 42.3k | entries[cur_group].deps = rev ? depgraph.Descendants(idx): depgraph.Ancestors(idx); Branch (1979:39): [True: 21.1k, False: 21.1k]
| 1980 | 42.3k | entries[cur_group].feerate = depgraph.FeeRate(idx); | 1981 | 42.3k | if (rev) entries[cur_group].feerate.fee = -entries[cur_group].feerate.fee; Branch (1981:17): [True: 21.1k, False: 21.1k]
| 1982 | 42.3k | entries[cur_group].prev_tx = NO_PREV_TX; // No previous transaction in group. | 1983 | 42.3k | entries[cur_group].first_tx = cur_group; // Transaction itself is first of group. | 1984 | | // Insert the new group at the back of the groups linked list. | 1985 | 42.3k | entries[cur_group].prev_group = entries[SENTINEL].prev_group; | 1986 | 42.3k | entries[SENTINEL].prev_group = cur_group; | 1987 | | | 1988 | | // Start merge/swap cycle. | 1989 | 42.3k | DepGraphIndex next_group = SENTINEL; // We inserted at the end, so next group is sentinel. | 1990 | 42.3k | DepGraphIndex prev_group = entries[cur_group].prev_group; | 1991 | | // Continue as long as the current group has higher feerate than the previous one. | 1992 | 49.6k | while (ByRatio{entries[cur_group].feerate} > ByRatio{entries[prev_group].feerate}) { Branch (1992:20): [True: 7.36k, False: 42.3k]
| 1993 | | // prev_group/cur_group/next_group refer to (the last transactions of) 3 | 1994 | | // consecutive entries in groups list. | 1995 | 7.36k | Assume(cur_group == entries[next_group].prev_group); | 1996 | 7.36k | Assume(prev_group == entries[cur_group].prev_group); | 1997 | | // The sentinel has empty feerate, which is neither higher or lower than other | 1998 | | // feerates. Thus, the while loop we are in here guarantees that cur_group and | 1999 | | // prev_group are not the sentinel. | 2000 | 7.36k | Assume(cur_group != SENTINEL); | 2001 | 7.36k | Assume(prev_group != SENTINEL); | 2002 | 7.36k | if (entries[cur_group].deps.Overlaps(entries[prev_group].group)) { Branch (2002:21): [True: 6.67k, False: 685]
| 2003 | | // There is a dependency between cur_group and prev_group; merge prev_group | 2004 | | // into cur_group. The group/deps/feerate fields of prev_group remain unchanged | 2005 | | // but become unused. | 2006 | 6.67k | entries[cur_group].group |= entries[prev_group].group; | 2007 | 6.67k | entries[cur_group].deps |= entries[prev_group].deps; | 2008 | 6.67k | entries[cur_group].feerate += entries[prev_group].feerate; | 2009 | | // Make the first of the current group point to the tail of the previous group. | 2010 | 6.67k | entries[entries[cur_group].first_tx].prev_tx = prev_group; | 2011 | | // The first of the previous group becomes the first of the newly-merged group. | 2012 | 6.67k | entries[cur_group].first_tx = entries[prev_group].first_tx; | 2013 | | // The previous group becomes whatever group was before the former one. | 2014 | 6.67k | prev_group = entries[prev_group].prev_group; | 2015 | 6.67k | entries[cur_group].prev_group = prev_group; | 2016 | 6.67k | } else { | 2017 | | // There is no dependency between cur_group and prev_group; swap them. | 2018 | 685 | DepGraphIndex preprev_group = entries[prev_group].prev_group; | 2019 | | // If PP, P, C, N were the old preprev, prev, cur, next groups, then the new | 2020 | | // layout becomes [PP, C, P, N]. Update prev_groups to reflect that order. | 2021 | 685 | entries[next_group].prev_group = prev_group; | 2022 | 685 | entries[prev_group].prev_group = cur_group; | 2023 | 685 | entries[cur_group].prev_group = preprev_group; | 2024 | | // The current group remains the same, but the groups before/after it have | 2025 | | // changed. | 2026 | 685 | next_group = prev_group; | 2027 | 685 | prev_group = preprev_group; | 2028 | 685 | } | 2029 | 7.36k | } | 2030 | 42.3k | } | 2031 | | | 2032 | | // Convert the entries back to linearization (overwriting the existing one). | 2033 | 778 | DepGraphIndex cur_group = entries[0].prev_group; | 2034 | 778 | DepGraphIndex done = 0; | 2035 | 36.4k | while (cur_group != SENTINEL) { Branch (2035:16): [True: 35.6k, False: 778]
| 2036 | 35.6k | DepGraphIndex cur_tx = cur_group; | 2037 | | // Traverse the transactions of cur_group (from back to front), and write them in the | 2038 | | // same order during odd passes, and reversed (front to back) in even passes. | 2039 | 35.6k | if (rev) { Branch (2039:17): [True: 17.8k, False: 17.8k]
| 2040 | 21.1k | do { | 2041 | 21.1k | *(linearization.begin() + (done++)) = cur_tx - 1; | 2042 | 21.1k | cur_tx = entries[cur_tx].prev_tx; | 2043 | 21.1k | } while (cur_tx != NO_PREV_TX); Branch (2043:26): [True: 3.33k, False: 17.8k]
| 2044 | 17.8k | } else { | 2045 | 21.1k | do { | 2046 | 21.1k | *(linearization.end() - (++done)) = cur_tx - 1; | 2047 | 21.1k | cur_tx = entries[cur_tx].prev_tx; | 2048 | 21.1k | } while (cur_tx != NO_PREV_TX); Branch (2048:26): [True: 3.33k, False: 17.8k]
| 2049 | 17.8k | } | 2050 | 35.6k | cur_group = entries[cur_group].prev_group; | 2051 | 35.6k | } | 2052 | 778 | Assume(done == linearization.size()); | 2053 | 778 | } | 2054 | 389 | } |
_ZN17cluster_linearize13PostLinearizeIN13bitset_detail9IntBitSetImEEEEvRKNS_8DepGraphIT_EESt4spanIjLm18446744073709551615EE Line | Count | Source | 1873 | 1.32M | { | 1874 | | // This algorithm performs a number of passes (currently 2); the even ones operate from back to | 1875 | | // front, the odd ones from front to back. Each results in an equal-or-better linearization | 1876 | | // than the one started from. | 1877 | | // - One pass in either direction guarantees that the resulting chunks are connected. | 1878 | | // - Each direction corresponds to one shape of tree being linearized optimally (forward passes | 1879 | | // guarantee this for graphs where each transaction has at most one child; backward passes | 1880 | | // guarantee this for graphs where each transaction has at most one parent). | 1881 | | // - Starting with a backward pass guarantees the moved-tree property. | 1882 | | // | 1883 | | // During an odd (forward) pass, the high-level operation is: | 1884 | | // - Start with an empty list of groups L=[]. | 1885 | | // - For every transaction i in the old linearization, from front to back: | 1886 | | // - Append a new group C=[i], containing just i, to the back of L. | 1887 | | // - While L has at least one group before C, and the group immediately before C has feerate | 1888 | | // lower than C: | 1889 | | // - If C depends on P: | 1890 | | // - Merge P into C, making C the concatenation of P+C, continuing with the combined C. | 1891 | | // - Otherwise: | 1892 | | // - Swap P with C, continuing with the now-moved C. | 1893 | | // - The output linearization is the concatenation of the groups in L. | 1894 | | // | 1895 | | // During even (backward) passes, i iterates from the back to the front of the existing | 1896 | | // linearization, and new groups are prepended instead of appended to the list L. To enable | 1897 | | // more code reuse, both passes append groups, but during even passes the meanings of | 1898 | | // parent/child, and of high/low feerate are reversed, and the final concatenation is reversed | 1899 | | // on output. | 1900 | | // | 1901 | | // In the implementation below, the groups are represented by singly-linked lists (pointing | 1902 | | // from the back to the front), which are themselves organized in a singly-linked circular | 1903 | | // list (each group pointing to its predecessor, with a special sentinel group at the front | 1904 | | // that points back to the last group). | 1905 | | // | 1906 | | // Information about transaction t is stored in entries[t + 1], while the sentinel is in | 1907 | | // entries[0]. | 1908 | | | 1909 | | /** Index of the sentinel in the entries array below. */ | 1910 | 1.32M | static constexpr DepGraphIndex SENTINEL{0}; | 1911 | | /** Indicator that a group has no previous transaction. */ | 1912 | 1.32M | static constexpr DepGraphIndex NO_PREV_TX{0}; | 1913 | | | 1914 | | | 1915 | | /** Data structure per transaction entry. */ | 1916 | 1.32M | struct TxEntry | 1917 | 1.32M | { | 1918 | | /** The index of the previous transaction in this group; NO_PREV_TX if this is the first | 1919 | | * entry of a group. */ | 1920 | 1.32M | DepGraphIndex prev_tx; | 1921 | | | 1922 | | // The fields below are only used for transactions that are the last one in a group | 1923 | | // (referred to as tail transactions below). | 1924 | | | 1925 | | /** Index of the first transaction in this group, possibly itself. */ | 1926 | 1.32M | DepGraphIndex first_tx; | 1927 | | /** Index of the last transaction in the previous group. The first group (the sentinel) | 1928 | | * points back to the last group here, making it a singly-linked circular list. */ | 1929 | 1.32M | DepGraphIndex prev_group; | 1930 | | /** All transactions in the group. Empty for the sentinel. */ | 1931 | 1.32M | SetType group; | 1932 | | /** All dependencies of the group (descendants in even passes; ancestors in odd ones). */ | 1933 | 1.32M | SetType deps; | 1934 | | /** The combined fee/size of transactions in the group. Fee is negated in even passes. */ | 1935 | 1.32M | FeeFrac feerate; | 1936 | 1.32M | }; | 1937 | | | 1938 | | // As an example, consider the state corresponding to the linearization [1,0,3,2], with | 1939 | | // groups [1,0,3] and [2], in an odd pass. The linked lists would be: | 1940 | | // | 1941 | | // +-----+ | 1942 | | // 0<-P-- | 0 S | ---\ Legend: | 1943 | | // +-----+ | | 1944 | | // ^ | - digit in box: entries index | 1945 | | // /--------------F---------+ G | (note: one more than tx value) | 1946 | | // v \ | | - S: sentinel group | 1947 | | // +-----+ +-----+ +-----+ | (empty feerate) | 1948 | | // 0<-P-- | 2 | <--P-- | 1 | <--P-- | 4 T | | - T: tail transaction, contains | 1949 | | // +-----+ +-----+ +-----+ | fields beyond prev_tv. | 1950 | | // ^ | - P: prev_tx reference | 1951 | | // G G - F: first_tx reference | 1952 | | // | | - G: prev_group reference | 1953 | | // +-----+ | | 1954 | | // 0<-P-- | 3 T | <--/ | 1955 | | // +-----+ | 1956 | | // ^ | | 1957 | | // \-F-/ | 1958 | | // | 1959 | | // During an even pass, the diagram above would correspond to linearization [2,3,0,1], with | 1960 | | // groups [2] and [3,0,1]. | 1961 | | | 1962 | 1.32M | std::vector<TxEntry> entries(depgraph.PositionRange() + 1); | 1963 | | | 1964 | | // Perform two passes over the linearization. | 1965 | 3.96M | for (int pass = 0; pass < 2; ++pass) { Branch (1965:24): [True: 2.64M, False: 1.32M]
| 1966 | 2.64M | int rev = !(pass & 1); | 1967 | | // Construct a sentinel group, identifying the start of the list. | 1968 | 2.64M | entries[SENTINEL].prev_group = SENTINEL; | 1969 | 2.64M | Assume(entries[SENTINEL].feerate.IsEmpty()); | 1970 | | | 1971 | | // Iterate over all elements in the existing linearization. | 1972 | 15.9M | for (DepGraphIndex i = 0; i < linearization.size(); ++i) { Branch (1972:35): [True: 13.3M, False: 2.64M]
| 1973 | | // Even passes are from back to front; odd passes from front to back. | 1974 | 13.3M | DepGraphIndex idx = linearization[rev ? linearization.size() - 1 - i : i]; Branch (1974:47): [True: 6.66M, False: 6.66M]
| 1975 | | // Construct a new group containing just idx. In even passes, the meaning of | 1976 | | // parent/child and high/low feerate are swapped. | 1977 | 13.3M | DepGraphIndex cur_group = idx + 1; | 1978 | 13.3M | entries[cur_group].group = SetType::Singleton(idx); | 1979 | 13.3M | entries[cur_group].deps = rev ? depgraph.Descendants(idx): depgraph.Ancestors(idx); Branch (1979:39): [True: 6.66M, False: 6.66M]
| 1980 | 13.3M | entries[cur_group].feerate = depgraph.FeeRate(idx); | 1981 | 13.3M | if (rev) entries[cur_group].feerate.fee = -entries[cur_group].feerate.fee; Branch (1981:17): [True: 6.66M, False: 6.66M]
| 1982 | 13.3M | entries[cur_group].prev_tx = NO_PREV_TX; // No previous transaction in group. | 1983 | 13.3M | entries[cur_group].first_tx = cur_group; // Transaction itself is first of group. | 1984 | | // Insert the new group at the back of the groups linked list. | 1985 | 13.3M | entries[cur_group].prev_group = entries[SENTINEL].prev_group; | 1986 | 13.3M | entries[SENTINEL].prev_group = cur_group; | 1987 | | | 1988 | | // Start merge/swap cycle. | 1989 | 13.3M | DepGraphIndex next_group = SENTINEL; // We inserted at the end, so next group is sentinel. | 1990 | 13.3M | DepGraphIndex prev_group = entries[cur_group].prev_group; | 1991 | | // Continue as long as the current group has higher feerate than the previous one. | 1992 | 17.6M | while (ByRatio{entries[cur_group].feerate} > ByRatio{entries[prev_group].feerate}) { Branch (1992:20): [True: 4.30M, False: 13.3M]
| 1993 | | // prev_group/cur_group/next_group refer to (the last transactions of) 3 | 1994 | | // consecutive entries in groups list. | 1995 | 4.30M | Assume(cur_group == entries[next_group].prev_group); | 1996 | 4.30M | Assume(prev_group == entries[cur_group].prev_group); | 1997 | | // The sentinel has empty feerate, which is neither higher or lower than other | 1998 | | // feerates. Thus, the while loop we are in here guarantees that cur_group and | 1999 | | // prev_group are not the sentinel. | 2000 | 4.30M | Assume(cur_group != SENTINEL); | 2001 | 4.30M | Assume(prev_group != SENTINEL); | 2002 | 4.30M | if (entries[cur_group].deps.Overlaps(entries[prev_group].group)) { Branch (2002:21): [True: 4.08M, False: 227k]
| 2003 | | // There is a dependency between cur_group and prev_group; merge prev_group | 2004 | | // into cur_group. The group/deps/feerate fields of prev_group remain unchanged | 2005 | | // but become unused. | 2006 | 4.08M | entries[cur_group].group |= entries[prev_group].group; | 2007 | 4.08M | entries[cur_group].deps |= entries[prev_group].deps; | 2008 | 4.08M | entries[cur_group].feerate += entries[prev_group].feerate; | 2009 | | // Make the first of the current group point to the tail of the previous group. | 2010 | 4.08M | entries[entries[cur_group].first_tx].prev_tx = prev_group; | 2011 | | // The first of the previous group becomes the first of the newly-merged group. | 2012 | 4.08M | entries[cur_group].first_tx = entries[prev_group].first_tx; | 2013 | | // The previous group becomes whatever group was before the former one. | 2014 | 4.08M | prev_group = entries[prev_group].prev_group; | 2015 | 4.08M | entries[cur_group].prev_group = prev_group; | 2016 | 4.08M | } else { | 2017 | | // There is no dependency between cur_group and prev_group; swap them. | 2018 | 227k | DepGraphIndex preprev_group = entries[prev_group].prev_group; | 2019 | | // If PP, P, C, N were the old preprev, prev, cur, next groups, then the new | 2020 | | // layout becomes [PP, C, P, N]. Update prev_groups to reflect that order. | 2021 | 227k | entries[next_group].prev_group = prev_group; | 2022 | 227k | entries[prev_group].prev_group = cur_group; | 2023 | 227k | entries[cur_group].prev_group = preprev_group; | 2024 | | // The current group remains the same, but the groups before/after it have | 2025 | | // changed. | 2026 | 227k | next_group = prev_group; | 2027 | 227k | prev_group = preprev_group; | 2028 | 227k | } | 2029 | 4.30M | } | 2030 | 13.3M | } | 2031 | | | 2032 | | // Convert the entries back to linearization (overwriting the existing one). | 2033 | 2.64M | DepGraphIndex cur_group = entries[0].prev_group; | 2034 | 2.64M | DepGraphIndex done = 0; | 2035 | 11.8M | while (cur_group != SENTINEL) { Branch (2035:16): [True: 9.24M, False: 2.64M]
| 2036 | 9.24M | DepGraphIndex cur_tx = cur_group; | 2037 | | // Traverse the transactions of cur_group (from back to front), and write them in the | 2038 | | // same order during odd passes, and reversed (front to back) in even passes. | 2039 | 9.24M | if (rev) { Branch (2039:17): [True: 4.62M, False: 4.62M]
| 2040 | 6.66M | do { | 2041 | 6.66M | *(linearization.begin() + (done++)) = cur_tx - 1; | 2042 | 6.66M | cur_tx = entries[cur_tx].prev_tx; | 2043 | 6.66M | } while (cur_tx != NO_PREV_TX); Branch (2043:26): [True: 2.04M, False: 4.62M]
| 2044 | 4.62M | } else { | 2045 | 6.66M | do { | 2046 | 6.66M | *(linearization.end() - (++done)) = cur_tx - 1; | 2047 | 6.66M | cur_tx = entries[cur_tx].prev_tx; | 2048 | 6.66M | } while (cur_tx != NO_PREV_TX); Branch (2048:26): [True: 2.04M, False: 4.62M]
| 2049 | 4.62M | } | 2050 | 9.24M | cur_group = entries[cur_group].prev_group; | 2051 | 9.24M | } | 2052 | 2.64M | Assume(done == linearization.size()); | 2053 | 2.64M | } | 2054 | 1.32M | } |
|
2055 | | |
2056 | | } // namespace cluster_linearize |
2057 | | |
2058 | | #endif // BITCOIN_CLUSTER_LINEARIZE_H |