@ -11,6 +11,7 @@
# include <util/bitset.h>
# include <util/feefrac.h>
# include <algorithm>
# include <stdint.h>
# include <vector>
# include <utility>
@ -140,6 +141,29 @@ public:
}
} ;
/** A simple linearization algorithm.
*
* This matches Linearize ( ) in interface and behavior , though with fewer optimizations , and using
* just SimpleCandidateFinder rather than AncestorCandidateFinder and SearchCandidateFinder .
*/
template < typename SetType >
std : : pair < std : : vector < ClusterIndex > , bool > SimpleLinearize ( const DepGraph < SetType > & depgraph , uint64_t max_iterations )
{
std : : vector < ClusterIndex > linearization ;
SimpleCandidateFinder finder ( depgraph ) ;
SetType todo = SetType : : Fill ( depgraph . TxCount ( ) ) ;
bool optimal = true ;
while ( todo . Any ( ) ) {
auto [ candidate , iterations_done ] = finder . FindCandidateSet ( max_iterations ) ;
if ( iterations_done = = max_iterations ) optimal = false ;
depgraph . AppendTopo ( linearization , candidate . transactions ) ;
todo - = candidate . transactions ;
finder . MarkDone ( candidate . transactions ) ;
max_iterations - = iterations_done ;
}
return { std : : move ( linearization ) , optimal } ;
}
/** Given a dependency graph, and a todo set, read a topological subset of todo from reader. */
template < typename SetType >
SetType ReadTopologicalSet ( const DepGraph < SetType > & depgraph , const SetType & todo , SpanReader & reader )
@ -458,3 +482,68 @@ FUZZ_TARGET(clusterlin_search_finder)
assert ( exh_finder . AllDone ( ) ) ;
assert ( anc_finder . AllDone ( ) ) ;
}
FUZZ_TARGET ( clusterlin_linearize )
{
// Verify the behavior of Linearize().
// Retrieve an iteration count, and a depgraph from the fuzz input.
SpanReader reader ( buffer ) ;
DepGraph < TestBitSet > depgraph ;
uint64_t iter_count { 0 } ;
try {
reader > > VARINT ( iter_count ) > > Using < DepGraphFormatter > ( depgraph ) ;
} catch ( const std : : ios_base : : failure & ) { }
// Invoke Linearize().
iter_count & = 0x7ffff ;
auto [ linearization , optimal ] = Linearize ( depgraph , iter_count ) ;
SanityCheck ( depgraph , linearization ) ;
auto chunking = ChunkLinearization ( depgraph , linearization ) ;
// If the iteration count is sufficiently high, an optimal linearization must be found.
// Each linearization step can use up to 2^k iterations, with steps k=1..n. That sum is
// 2 * (2^n - 1)
const uint64_t n = depgraph . TxCount ( ) ;
if ( n < = 18 & & iter_count > 2U * ( ( uint64_t { 1 } < < n ) - 1U ) ) {
assert ( optimal ) ;
}
// If Linearize claims optimal result, run quality tests.
if ( optimal ) {
// It must be as good as SimpleLinearize.
auto [ simple_linearization , simple_optimal ] = SimpleLinearize ( depgraph , MAX_SIMPLE_ITERATIONS ) ;
SanityCheck ( depgraph , simple_linearization ) ;
auto simple_chunking = ChunkLinearization ( depgraph , simple_linearization ) ;
auto cmp = CompareChunks ( chunking , simple_chunking ) ;
assert ( cmp > = 0 ) ;
// If SimpleLinearize finds the optimal result too, they must be equal (if not,
// SimpleLinearize is broken).
if ( simple_optimal ) assert ( cmp = = 0 ) ;
// Only for very small clusters, test every topologically-valid permutation.
if ( depgraph . TxCount ( ) < = 7 ) {
std : : vector < ClusterIndex > perm_linearization ( depgraph . TxCount ( ) ) ;
for ( ClusterIndex i = 0 ; i < depgraph . TxCount ( ) ; + + i ) perm_linearization [ i ] = i ;
// Iterate over all valid permutations.
do {
// Determine whether perm_linearization is topological.
TestBitSet perm_done ;
bool perm_is_topo { true } ;
for ( auto i : perm_linearization ) {
perm_done . Set ( i ) ;
if ( ! depgraph . Ancestors ( i ) . IsSubsetOf ( perm_done ) ) {
perm_is_topo = false ;
break ;
}
}
// If so, verify that the obtained linearization is as good as the permutation.
if ( perm_is_topo ) {
auto perm_chunking = ChunkLinearization ( depgraph , perm_linearization ) ;
auto cmp = CompareChunks ( chunking , perm_chunking ) ;
assert ( cmp > = 0 ) ;
}
} while ( std : : next_permutation ( perm_linearization . begin ( ) , perm_linearization . end ( ) ) ) ;
}
}
}