From 3d3b9985c1f9bdd3d120e98a06e47c4ac3a7544b Mon Sep 17 00:00:00 2001 From: Heinz Riener Date: Wed, 25 Nov 2020 18:54:34 +0100 Subject: [PATCH 1/5] window_rewriting. --- experiments/window_rewriting.cpp | 2 +- .../algorithms/window_rewriting.hpp | 86 +++++++++++++------ include/mockturtle/utils/debugging_utils.hpp | 51 +++++++++++ 3 files changed, 114 insertions(+), 25 deletions(-) diff --git a/experiments/window_rewriting.cpp b/experiments/window_rewriting.cpp index 63452003b..77a2a1672 100644 --- a/experiments/window_rewriting.cpp +++ b/experiments/window_rewriting.cpp @@ -44,7 +44,7 @@ int main() experiment exp( "window_rewriting", "benchmark", "size_before", "size_after", "runtime", "resubs", "equivalent" ); - for ( auto const& benchmark : epfl_benchmarks() ) + for ( auto const& benchmark : all_benchmarks( iscas | epfl ) ) { fmt::print( "[i] processing {}\n", benchmark ); diff --git a/include/mockturtle/algorithms/window_rewriting.hpp b/include/mockturtle/algorithms/window_rewriting.hpp index 8c5931f53..87b1807de 100644 --- a/include/mockturtle/algorithms/window_rewriting.hpp +++ b/include/mockturtle/algorithms/window_rewriting.hpp @@ -63,6 +63,46 @@ struct window_rewriting_stats namespace detail { +template +bool is_contained_in_tfi_recursive( Ntk const& ntk, typename Ntk::node const& node, typename Ntk::node const& n ) +{ + if ( ntk.color( node ) == ntk.current_color() ) + { + return false; + } + ntk.paint( node ); + + if ( n == node ) + { + return true; + } + + bool found = false; + ntk.foreach_fanin( node, [&]( typename Ntk::signal const& fi ){ + if ( is_contained_in_tfi_recursive( ntk, ntk.get_node( fi ), n ) ) + { + found = true; + return false; + } + return true; + }); + + return found; +} + +} /* namespace detail */ + +template +bool is_contained_in_tfi( Ntk const& ntk, typename Ntk::node const& node, typename Ntk::node const& n ) +{ + /* do not even build the TFI and just search for the node */ + ntk.new_color(); + return is_contained_in_tfi_recursive( ntk, node, n ); +} + +namespace detail +{ + template class window_rewriting_impl { @@ -137,41 +177,43 @@ class window_rewriting_impl std::vector new_outputs; uint32_t counter{0}; - bool substitution_failure = false; ++st.num_substitutions; insert( ntk, std::begin( signals ), std::end( signals ), *il_opt, [&]( signal const& _new ) { auto const _old = outputs.at( counter++ ); - if ( substitution_failure ) - { - if ( ntk.fanout_size( ntk.get_node( _new ) ) == 0 ) - { - ntk.take_out_node( ntk.get_node( _new ) ); - } - return true; - } if ( _old == _new ) { return true; } - else if ( ntk.level( ntk.get_node( _old ) ) >= ntk.level( ntk.get_node( _new ) ) ) - { - auto const updates = substitute_node( ntk.get_node( _old ), topo_win.is_complemented( _old ) ? !_new : _new ); - update_vector( outputs, updates ); - } - else + + /* ensure that _old is not in the TFI of _new */ + // assert( !is_contained_in_tfi( ntk, ntk.get_node( _new ), ntk.get_node( _old ) ) ); + if ( is_contained_in_tfi( ntk, ntk.get_node( _new ), ntk.get_node( _old ) ) ) { - if ( ntk.fanout_size( ntk.get_node( _new ) ) == 0 ) - { - ntk.take_out_node( ntk.get_node( _new ) ); - } - substitution_failure = true; + std::cout << "undo resubstitution " << ntk.get_node( _old ) << std::endl; + ntk.take_out_node( ntk.get_node( _new ) ); + return false; } + + auto const updates = substitute_node( ntk.get_node( _old ), topo_win.is_complemented( _old ) ? !_new : _new ); + update_vector( outputs, updates ); return true; }); + /* recompute levels and depth */ + ntk.update_levels(); + + /* ensure that no dead nodes are reachable */ + assert( count_reachable_dead_nodes( ntk ) == 0u ); + + /* ensure that the network structure is still acyclic */ + assert( network_is_acylic( ntk ) ); + + /* ensure that the levels and depth is correct */ + assert( check_network_levels( ntk ) ); + /* update internal data structures in windowing */ windowing.resize( ntk.size() ); } @@ -301,10 +343,6 @@ class window_rewriting_impl if ( curr_level != max_level ) { ntk.set_level( n, max_level ); - if ( max_level >= ntk.depth() ) - { - ntk.set_depth( max_level + 1 ); - } /* update only one more level */ if ( top_most ) diff --git a/include/mockturtle/utils/debugging_utils.hpp b/include/mockturtle/utils/debugging_utils.hpp index 405545b04..c54028b0a 100644 --- a/include/mockturtle/utils/debugging_utils.hpp +++ b/include/mockturtle/utils/debugging_utils.hpp @@ -202,4 +202,55 @@ bool network_is_acylic( Ntk const& ntk ) return result; } +template +bool check_network_levels( Ntk const& ntk ) +{ + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_size_v, "Ntk does not implement the size function" ); + static_assert( has_is_constant_v, "Ntk does not implement the is_constant function" ); + static_assert( has_is_ci_v, "Ntk does not implement the is_ci function" ); + // static_assert( has_is_dead_v, "Ntk does not implement the is_dead function" ); + static_assert( has_foreach_fanin_v, "Ntk does not implement the foreach_fanin function" ); + static_assert( has_level_v, "Ntk does not implement the level function" ); + static_assert( has_depth_v, "Ntk does not implement the depth function" ); + + using signal = typename Ntk::signal; + + uint32_t max = 0; + for ( uint32_t i = 0u; i < ntk.size(); ++i ) + { + if ( ntk.is_constant( i ) || ntk.is_ci( i ) || ntk.is_dead( i ) ) + { + continue; + } + + uint32_t max_fanin_level = 0; + ntk.foreach_fanin( i, [&]( signal fi ){ + if ( ntk.level( ntk.get_node( fi ) ) > max_fanin_level ) + { + max_fanin_level = ntk.level( ntk.get_node( fi ) ); + } + }); + + /* the node's level has not been correctly computed */ + if ( ntk.level( i ) != max_fanin_level + 1 ) + { + return false; + } + + if ( ntk.level( i ) > max ) + { + max = ntk.level( i ); + } + } + + /* the network's depth has not been correctly computed */ + if ( ntk.depth() != max ) + { + return false; + } + + return true; +} + } /* namespace mockturtle */ From d8b1d8e3e2f65627dbe0160d66d4cfb333dfc9b2 Mon Sep 17 00:00:00 2001 From: Heinz Riener Date: Wed, 25 Nov 2020 19:56:16 +0100 Subject: [PATCH 2/5] runtime profiling. --- experiments/window_rewriting.cpp | 9 ++- .../algorithms/window_rewriting.hpp | 81 +++++++++++++++++-- 2 files changed, 79 insertions(+), 11 deletions(-) diff --git a/experiments/window_rewriting.cpp b/experiments/window_rewriting.cpp index 77a2a1672..bc78ecc45 100644 --- a/experiments/window_rewriting.cpp +++ b/experiments/window_rewriting.cpp @@ -41,8 +41,8 @@ int main() using namespace experiments; using namespace mockturtle; - experiment - exp( "window_rewriting", "benchmark", "size_before", "size_after", "runtime", "resubs", "equivalent" ); + experiment + exp( "window_rewriting", "benchmark", "size_before", "size_after", "est. gain", "real gain", "runtime", "resubs", "equivalent" ); for ( auto const& benchmark : all_benchmarks( iscas | epfl ) ) { @@ -66,6 +66,7 @@ int main() window_rewriting_params ps; ps.cut_size = 6u; ps.num_levels = 5u; + ps.filter_cyclic_substitutions = benchmark == "c432" ? true : false; window_rewriting_stats st; window_rewriting( aig, ps, &st ); @@ -73,7 +74,9 @@ int main() auto const cec = benchmark != "hyp" ? abc_cec( ntk, benchmark ) : true; - exp( benchmark, size_before, ntk.num_gates(), to_seconds( st.time_total ), + st.report(); + + exp( benchmark, size_before, ntk.num_gates(), st.gain, size_before - ntk.num_gates(), to_seconds( st.time_total ), st.num_substitutions, cec ); } diff --git a/include/mockturtle/algorithms/window_rewriting.hpp b/include/mockturtle/algorithms/window_rewriting.hpp index 87b1807de..591739883 100644 --- a/include/mockturtle/algorithms/window_rewriting.hpp +++ b/include/mockturtle/algorithms/window_rewriting.hpp @@ -49,6 +49,8 @@ struct window_rewriting_params { uint64_t cut_size{6}; uint64_t num_levels{5}; + + bool filter_cyclic_substitutions{false}; }; /* window_rewriting_params */ struct window_rewriting_stats @@ -56,8 +58,56 @@ struct window_rewriting_stats /*! \brief Total runtime. */ stopwatch<>::duration time_total{0}; + /*! \brief Time for constructing windows. */ + stopwatch<>::duration time_window{0}; + + /*! \brief Time for optimizing windows. */ + stopwatch<>::duration time_optimize{0}; + + /*! \brief Time for substituting. */ + stopwatch<>::duration time_substitute{0}; + + /*! \brief Time for updating level information. */ + stopwatch<>::duration time_levels{0}; + + /*! \brief Time for updating window outputs. */ + stopwatch<>::duration time_update_vector{0}; + + /*! \brief Time for topological sorting. */ + stopwatch<>::duration time_topo_sort{0}; + + /*! \brief Time for encoding index_list. */ + stopwatch<>::duration time_encode{0}; + /*! \brief Total number of calls to the resub. engine. */ uint64_t num_substitutions{0}; + uint64_t num_restrashes{0}; + uint64_t num_windows{0}; + uint64_t gain{0}; + + void report() const + { + stopwatch<>::duration time_other = + time_total - time_window - time_topo_sort - time_optimize - time_substitute - time_levels - time_update_vector; + + fmt::print( "===========================================================================\n" ); + fmt::print( "[i] Windowing = {:7.2f} ({:5.2f}%) (#win = {})\n", + to_seconds( time_window ), to_seconds( time_window ) / to_seconds( time_total ) * 100, num_windows ); + fmt::print( "[i] Top.sort = {:7.2f} ({:5.2f}%)\n", to_seconds( time_topo_sort ), to_seconds( time_topo_sort ) / to_seconds( time_total ) * 100 ); + fmt::print( "[i] Enc.list = {:7.2f} ({:5.2f}%)\n", to_seconds( time_encode ), to_seconds( time_encode ) / to_seconds( time_total ) * 100 ); + fmt::print( "[i] Optimize = {:7.2f} ({:5.2f}%) (#resubs = {}, est. gain = {})\n", + to_seconds( time_optimize ), to_seconds( time_optimize ) / to_seconds( time_total ) * 100, num_substitutions, gain ); + fmt::print( "[i] Substitute = {:7.2f} ({:5.2f}%) (#hash upd. = {})\n", + to_seconds( time_substitute ), + to_seconds( time_substitute ) / to_seconds( time_total ) * 100, + num_restrashes ); + fmt::print( "[i] Upd.levels = {:7.2f} ({:5.2f}%)\n", to_seconds( time_levels ), to_seconds( time_levels ) / to_seconds( time_total ) * 100 ); + fmt::print( "[i] Upd.win = {:7.2f} ({:5.2f}%)\n", to_seconds( time_update_vector ), to_seconds( time_update_vector ) / to_seconds( time_total ) * 100 ); + fmt::print( "[i] Other = {:7.2f} ({:5.2f}%)\n", to_seconds( time_other ), to_seconds( time_other ) / to_seconds( time_total ) * 100 ); + fmt::print( "---------------------------------------------------------------------------\n" ); + fmt::print( "[i] TOTAL = {:7.2f}\n", to_seconds( time_total ) ); + fmt::print( "===========================================================================\n" ); + } }; /* window_rewriting_stats */ namespace detail @@ -95,9 +145,9 @@ bool is_contained_in_tfi_recursive( Ntk const& ntk, typename Ntk::node const& no template bool is_contained_in_tfi( Ntk const& ntk, typename Ntk::node const& node, typename Ntk::node const& n ) { - /* do not even build the TFI and just search for the node */ + /* do not even build the TFI, but just search for the node */ ntk.new_color(); - return is_contained_in_tfi_recursive( ntk, node, n ); + return detail::is_contained_in_tfi_recursive( ntk, node, n ); } namespace detail @@ -150,13 +200,20 @@ class window_rewriting_impl continue; } - if ( const auto w = windowing.run( n, ps.cut_size, ps.num_levels ) ) + if ( const auto w = call_with_stopwatch( st.time_window, [&]() { return windowing.run( n, ps.cut_size, ps.num_levels ); } ) ) { - window_view win( ntk, w->inputs, w->outputs, w->nodes ); - topo_view topo_win{win}; + ++st.num_windows; + + auto topo_win = call_with_stopwatch( st.time_topo_sort, ( [&](){ + window_view win( ntk, w->inputs, w->outputs, w->nodes ); + topo_view topo_win{win}; + return topo_win; + }) ); abc_index_list il; - encode( il, topo_win ); + call_with_stopwatch( st.time_encode, [&]() { + encode( il, topo_win ); + } ); auto il_opt = optimize( il ); if ( !il_opt ) @@ -190,7 +247,7 @@ class window_rewriting_impl /* ensure that _old is not in the TFI of _new */ // assert( !is_contained_in_tfi( ntk, ntk.get_node( _new ), ntk.get_node( _old ) ) ); - if ( is_contained_in_tfi( ntk, ntk.get_node( _new ), ntk.get_node( _old ) ) ) + if ( ps.filter_cyclic_substitutions && is_contained_in_tfi( ntk, ntk.get_node( _new ), ntk.get_node( _old ) ) ) { std::cout << "undo resubstitution " << ntk.get_node( _old ) << std::endl; ntk.take_out_node( ntk.get_node( _new ) ); @@ -203,7 +260,7 @@ class window_rewriting_impl }); /* recompute levels and depth */ - ntk.update_levels(); + call_with_stopwatch( st.time_levels, [&]() { ntk.update_levels(); } ); /* ensure that no dead nodes are reachable */ assert( count_reachable_dead_nodes( ntk ) == 0u ); @@ -226,6 +283,8 @@ class window_rewriting_impl /* optimize an index_list and return the new list */ std::optional optimize( abc_index_list const& il, bool verbose = false ) { + stopwatch t( st.time_optimize ); + int *raw = ABC_CALLOC( int, il.size() + 1u ); uint64_t i = 0; for ( auto const& v : il.raw() ) @@ -245,6 +304,7 @@ class window_rewriting_impl fmt::print( "Performed resub {} times. Reduced {} nodes.\n", num_resubs, new_entries > 0 ? ( ( il.size() / 2u ) - new_entries ) : 0 ); } + st.gain += new_entries > 0 ? ( ( il.size() / 2u ) - new_entries ) : 0; if ( raw ) { @@ -275,6 +335,8 @@ class window_rewriting_impl /* substitute the node with a signal and return all strashing updates */ std::vector> substitute_node( node const& old_node, signal const& new_signal ) { + stopwatch t( st.time_substitute ); + std::vector> updates; std::stack> to_substitute; to_substitute.push( {old_node, new_signal} ); @@ -297,6 +359,7 @@ class window_rewriting_impl if ( const auto repl = ntk.replace_in_node( idx, _old, _new ); repl ) { to_substitute.push( *repl ); + ++st.num_restrashes; } } @@ -312,6 +375,8 @@ class window_rewriting_impl void update_vector( std::vector& vs, std::vector> const& updates ) { + stopwatch t( st.time_update_vector ); + for ( auto it = std::begin( vs ); it != std::end( vs ); ++it ) { for ( auto it2 = std::begin( updates ); it2 != std::end( updates ); ++it2 ) From 2b41de48641ae353f215a911584617a1fa351c7b Mon Sep 17 00:00:00 2001 From: Heinz Riener Date: Wed, 25 Nov 2020 21:33:43 +0100 Subject: [PATCH 3/5] window_rewriting: better performance for substitute_node. --- include/mockturtle/algorithms/window_rewriting.hpp | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/include/mockturtle/algorithms/window_rewriting.hpp b/include/mockturtle/algorithms/window_rewriting.hpp index 591739883..a428a3920 100644 --- a/include/mockturtle/algorithms/window_rewriting.hpp +++ b/include/mockturtle/algorithms/window_rewriting.hpp @@ -351,12 +351,10 @@ class window_rewriting_impl updates.push_back( p ); } - for ( auto idx = 1u; idx < ntk._storage->nodes.size(); ++idx ) + const auto parents = ntk.fanout( _old ); + for ( auto n : parents ) { - if ( ntk.is_ci( idx ) || ntk.is_dead( idx ) ) - continue; /* ignore CIs */ - - if ( const auto repl = ntk.replace_in_node( idx, _old, _new ); repl ) + if ( const auto repl = ntk.replace_in_node( n, _old, _new ); repl ) { to_substitute.push( *repl ); ++st.num_restrashes; From f7dcd644f8c3f437d077bf5e081b82ca31edcc02 Mon Sep 17 00:00:00 2001 From: Heinz Riener Date: Thu, 26 Nov 2020 10:28:03 +0100 Subject: [PATCH 4/5] window_rewriting: only take out node if it is not used. --- include/mockturtle/algorithms/window_rewriting.hpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/include/mockturtle/algorithms/window_rewriting.hpp b/include/mockturtle/algorithms/window_rewriting.hpp index a428a3920..a4cb6f759 100644 --- a/include/mockturtle/algorithms/window_rewriting.hpp +++ b/include/mockturtle/algorithms/window_rewriting.hpp @@ -250,7 +250,10 @@ class window_rewriting_impl if ( ps.filter_cyclic_substitutions && is_contained_in_tfi( ntk, ntk.get_node( _new ), ntk.get_node( _old ) ) ) { std::cout << "undo resubstitution " << ntk.get_node( _old ) << std::endl; - ntk.take_out_node( ntk.get_node( _new ) ); + if ( ntk.fanout_size( ntk.get_node( _new ) ) == 0u ) + { + ntk.take_out_node( ntk.get_node( _new ) ); + } return false; } From b8bc3220b5ab82bfcc25bacdcd0749713e26c70e Mon Sep 17 00:00:00 2001 From: Heinz Riener Date: Fri, 16 Apr 2021 20:41:59 +0200 Subject: [PATCH 5/5] window_rewriting: improve performance of level update. --- .../algorithms/window_rewriting.hpp | 20 ++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/include/mockturtle/algorithms/window_rewriting.hpp b/include/mockturtle/algorithms/window_rewriting.hpp index a4cb6f759..42a37799e 100644 --- a/include/mockturtle/algorithms/window_rewriting.hpp +++ b/include/mockturtle/algorithms/window_rewriting.hpp @@ -192,7 +192,7 @@ class window_rewriting_impl stopwatch t( st.time_total ); create_window_impl windowing( ntk ); - uint32_t const size = 3*ntk.size(); + uint32_t const size = ntk.size(); for ( uint32_t n = 0u; n < std::min( size, ntk.size() ); ++n ) { if ( ntk.is_constant( n ) || ntk.is_ci( n ) || ntk.is_dead( n ) ) @@ -279,6 +279,7 @@ class window_rewriting_impl } } + /* ensure that no dead nodes are reachable */ assert( count_reachable_dead_nodes( ntk ) == 0u ); } @@ -393,7 +394,7 @@ class window_rewriting_impl /* recursively update the node levels and the depth of the critical path */ void update_node_level( node const& n, bool top_most = true ) { - uint32_t curr_level = ntk.level( n ); + uint32_t const curr_level = ntk.level( n ); uint32_t max_level = 0; ntk.foreach_fanin( n, [&]( const auto& f ) { @@ -406,17 +407,18 @@ class window_rewriting_impl } ); ++max_level; + if ( ntk.depth() < max_level ) + { + ntk.set_depth( max_level ); + } + if ( curr_level != max_level ) { ntk.set_level( n, max_level ); - /* update only one more level */ - if ( top_most ) - { - ntk.foreach_fanout( n, [&]( const auto& p ) { - update_node_level( p, false ); - } ); - } + ntk.foreach_fanout( n, [&]( const auto& p ) { + update_node_level( p, false ); + } ); } }