@@ -263,7 +263,7 @@ class TxGraphImpl final : public TxGraph
263263 {
264264 /* * The Entry which is the last transaction of the chunk. */
265265 mutable GraphIndex m_graph_index;
266- /* * How many transactions the chunk contains. */
266+ /* * How many transactions the chunk contains (-1 = singleton tail of cluster) . */
267267 LinearizationIndex m_chunk_count;
268268
269269 ChunkData (GraphIndex graph_index, LinearizationIndex chunk_count) noexcept :
@@ -681,7 +681,7 @@ void Cluster::Updated(TxGraphImpl& graph) noexcept
681681 // Iterate over the chunks.
682682 for (unsigned chunk_idx = 0 ; chunk_idx < chunking.NumChunksLeft (); ++chunk_idx) {
683683 auto chunk = chunking.GetChunk (chunk_idx);
684- const auto chunk_count = chunk.transactions .Count ();
684+ auto chunk_count = chunk.transactions .Count ();
685685 Assume (chunk_count > 0 );
686686 // Iterate over the transactions in the linearization, which must match those in chunk.
687687 while (true ) {
@@ -694,6 +694,12 @@ void Cluster::Updated(TxGraphImpl& graph) noexcept
694694 chunk.transactions .Reset (idx);
695695 if (chunk.transactions .None ()) {
696696 // Last transaction in the chunk.
697+ if (chunk_count == 1 && chunk_idx + 1 == chunking.NumChunksLeft ()) {
698+ // If this is the final chunk of the cluster, and it contains just a single
699+ // transaction (which will always be true for the very common singleton
700+ // clusters), store the special value -1 as chunk count.
701+ chunk_count = LinearizationIndex (-1 );
702+ }
697703 graph.CreateChunkData (graph_idx, chunk_count);
698704 break ;
699705 }
@@ -2152,7 +2158,11 @@ void Cluster::SanityCheck(const TxGraphImpl& graph, int level) const
21522158 assert ((entry.m_main_chunkindex_iterator != graph.m_main_chunkindex .end ()) == is_chunk_end);
21532159 if (is_chunk_end) {
21542160 auto & chunk_data = *entry.m_main_chunkindex_iterator ;
2155- assert (chunk_data.m_chunk_count == chunk_pos);
2161+ if (m_done == m_depgraph.Positions () && chunk_pos == 1 ) {
2162+ assert (chunk_data.m_chunk_count == LinearizationIndex (-1 ));
2163+ } else {
2164+ assert (chunk_data.m_chunk_count == chunk_pos);
2165+ }
21562166 }
21572167 // If this Cluster has an acceptable quality level, its chunks must be connected.
21582168 assert (m_depgraph.IsConnected (linchunking.GetChunk (0 ).transactions ));
@@ -2357,10 +2367,22 @@ std::optional<std::pair<std::vector<TxGraph::Ref*>, FeePerWeight>> BlockBuilderI
23572367 ret.emplace ();
23582368 const auto & chunk_data = *m_cur_iter;
23592369 const auto & chunk_end_entry = m_graph->m_entries [chunk_data.m_graph_index ];
2360- ret->first .resize (chunk_data.m_chunk_count );
2361- auto start_pos = chunk_end_entry.m_main_lin_index + 1 - chunk_data.m_chunk_count ;
2362- Assume (m_cur_cluster);
2363- m_known_end_of_cluster = m_cur_cluster->GetClusterRefs (*m_graph, ret->first , start_pos);
2370+ if (chunk_data.m_chunk_count == LinearizationIndex (-1 )) {
2371+ // Special case in case just a single transaction remains, avoiding the need to
2372+ // dispatch to and dereference Cluster.
2373+ ret->first .resize (1 );
2374+ Assume (chunk_end_entry.m_ref != nullptr );
2375+ ret->first [0 ] = chunk_end_entry.m_ref ;
2376+ m_known_end_of_cluster = true ;
2377+ } else {
2378+ Assume (m_cur_cluster);
2379+ ret->first .resize (chunk_data.m_chunk_count );
2380+ auto start_pos = chunk_end_entry.m_main_lin_index + 1 - chunk_data.m_chunk_count ;
2381+ m_known_end_of_cluster = m_cur_cluster->GetClusterRefs (*m_graph, ret->first , start_pos);
2382+ // If the chunk size was 1 and at end of cluster, then the special case above should
2383+ // have been used.
2384+ Assume (!m_known_end_of_cluster || chunk_data.m_chunk_count > 1 );
2385+ }
23642386 ret->second = chunk_end_entry.m_main_chunk_feerate ;
23652387 }
23662388 return ret;
@@ -2428,10 +2450,17 @@ std::pair<std::vector<TxGraph::Ref*>, FeePerWeight> TxGraphImpl::GetWorstMainChu
24282450 const auto & chunk_data = *m_main_chunkindex.rbegin ();
24292451 const auto & chunk_end_entry = m_entries[chunk_data.m_graph_index ];
24302452 Cluster* cluster = chunk_end_entry.m_locator [0 ].cluster ;
2431- ret.first .resize (chunk_data.m_chunk_count );
2432- auto start_pos = chunk_end_entry.m_main_lin_index + 1 - chunk_data.m_chunk_count ;
2433- cluster->GetClusterRefs (*this , ret.first , start_pos);
2434- std::reverse (ret.first .begin (), ret.first .end ());
2453+ if (chunk_data.m_chunk_count == LinearizationIndex (-1 ) || chunk_data.m_chunk_count == 1 ) {
2454+ // Special case for singletons.
2455+ ret.first .resize (1 );
2456+ Assume (chunk_end_entry.m_ref != nullptr );
2457+ ret.first [0 ] = chunk_end_entry.m_ref ;
2458+ } else {
2459+ ret.first .resize (chunk_data.m_chunk_count );
2460+ auto start_pos = chunk_end_entry.m_main_lin_index + 1 - chunk_data.m_chunk_count ;
2461+ cluster->GetClusterRefs (*this , ret.first , start_pos);
2462+ std::reverse (ret.first .begin (), ret.first .end ());
2463+ }
24352464 ret.second = chunk_end_entry.m_main_chunk_feerate ;
24362465 }
24372466 return ret;
0 commit comments