From d54aa2513778fd291c48c4a57d89509816619e8d Mon Sep 17 00:00:00 2001 From: Heinz Riener Date: Mon, 19 Apr 2021 18:43:52 +0200 Subject: [PATCH 01/10] improved performance of window-rewriting. --- .../algorithms/network_fuzz_tester.hpp | 2 +- .../algorithms/window_rewriting.hpp | 159 ++++++++++++------ 2 files changed, 107 insertions(+), 54 deletions(-) diff --git a/include/mockturtle/algorithms/network_fuzz_tester.hpp b/include/mockturtle/algorithms/network_fuzz_tester.hpp index 58d61e449..47c9fecab 100644 --- a/include/mockturtle/algorithms/network_fuzz_tester.hpp +++ b/include/mockturtle/algorithms/network_fuzz_tester.hpp @@ -108,7 +108,7 @@ class network_fuzz_tester } } - template + template void rerun_on_benchmark( Fn&& fn ) { /* read benchmark from a file */ diff --git a/include/mockturtle/algorithms/window_rewriting.hpp b/include/mockturtle/algorithms/window_rewriting.hpp index f65e71a43..da6d2743b 100644 --- a/include/mockturtle/algorithms/window_rewriting.hpp +++ b/include/mockturtle/algorithms/window_rewriting.hpp @@ -71,9 +71,6 @@ struct window_rewriting_stats /*! \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}; @@ -93,7 +90,6 @@ struct window_rewriting_stats time_optimize += other.time_optimize; time_substitute += other.time_substitute; time_levels += other.time_levels; - time_update_vector += other.time_update_vector; time_topo_sort += other.time_topo_sort; time_encode += other.time_encode; num_substitutions += other.num_substitutions; @@ -105,7 +101,7 @@ struct window_rewriting_stats void report() const { stopwatch<>::duration time_other = - time_total - time_window - time_topo_sort - time_optimize - time_substitute - time_levels - time_update_vector; + time_total - time_window - time_topo_sort - time_optimize - time_substitute - time_levels; fmt::print( "===========================================================================\n" ); fmt::print( "[i] Windowing = {:7.2f} ({:5.2f}%) (#win = {})\n", @@ -119,7 +115,6 @@ struct window_rewriting_stats 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 ) ); @@ -249,13 +244,14 @@ class window_rewriting_impl outputs.push_back( o ); }); - std::vector new_outputs; uint32_t counter{0}; - ++st.num_substitutions; + + std::list> substitutions; insert( ntk, std::begin( signals ), std::end( signals ), *il_opt, [&]( signal const& _new ) { + assert( !ntk.is_dead( ntk.get_node( _new ) ) ); auto const _old = outputs.at( counter++ ); if ( _old == _new ) { @@ -274,13 +270,15 @@ class window_rewriting_impl return false; } - auto const updates = substitute_node( ntk.get_node( _old ), topo_win.is_complemented( _old ) ? !_new : _new ); - update_vector( outputs, updates ); + substitutions.emplace_back( std::make_pair( ntk.get_node( _old ), ntk.is_complemented( _old ) ? !_new : _new ) ); return true; }); + substitute_nodes( substitutions ); + /* recompute levels and depth */ // call_with_stopwatch( st.time_levels, [&]() { ntk.update_levels(); } ); + update_depth(); /* ensure that no dead nodes are reachable */ assert( count_reachable_dead_nodes( ntk ) == 0u ); @@ -353,59 +351,100 @@ 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 ) + void substitute_nodes( std::list> substitutions ) { stopwatch t( st.time_substitute ); - std::vector> updates; - std::stack> to_substitute; - to_substitute.push( {old_node, new_signal} ); - while ( !to_substitute.empty() ) + auto clean_substitutions = [&]( node const& n ) { - const auto [_old, _new] = to_substitute.top(); - to_substitute.pop(); + substitutions.erase( std::remove_if( std::begin( substitutions ), std::end( substitutions ), + [&]( auto const& s ){ + if ( s.first == n ) + { + node const nn = ntk.get_node( s.second ); + if ( ntk.is_dead( nn ) ) + return true; + + /* deref fanout_size of the node */ + if ( ntk.fanout_size( nn ) > 0 ) + { + ntk.decr_fanout_size( nn ); + } + /* remove the node if it's fanout_size becomes 0 */ + if ( ntk.fanout_size( nn ) == 0 ) + { + ntk.take_out_node( nn ); + } + /* remove substitution from list */ + return true; + } + return false; /* keep */ + } ), + std::end( substitutions ) ); + }; - auto const p = std::make_pair( _old, _new ); - if ( std::find( std::begin( updates ), std::end( updates ), p ) == std::end( updates ) ) - { - updates.push_back( p ); - } + /* register event to delete substitutions if their right-hand side + nodes get deleted */ + ntk._events->on_delete.push_back( clean_substitutions ); - const auto parents = ntk.fanout( _old ); - for ( auto n : parents ) + /* increment fanout_size of all signals to be used in + substitutions to ensure that they will not be deleted */ + for ( const auto& s : substitutions ) + { + ntk.incr_fanout_size( ntk.get_node( s.second ) ); + } + + while ( !substitutions.empty() ) + { + auto const [old_node, new_signal] = substitutions.front(); + substitutions.pop_front(); + + // for ( auto index = 1u; index < _storage->nodes.size(); ++index ) + const auto parents = ntk.fanout( old_node ); + for ( auto index : parents ) { - if ( const auto repl = ntk.replace_in_node( n, _old, _new ); repl ) + /* skip CIs and dead nodes */ + if ( ntk.is_dead( index ) ) + continue; + + /* skip nodes that will be deleted */ + if ( std::find_if( std::begin( substitutions ), std::end( substitutions ), + [&index]( auto s ){ return s.first == index; } ) != std::end( substitutions ) ) + continue; + + /* replace in node */ + if ( const auto repl = ntk.replace_in_node( index, old_node, new_signal ); repl ) { - to_substitute.push( *repl ); - ++st.num_restrashes; + ntk.incr_fanout_size( ntk.get_node( repl->second ) ); + substitutions.emplace_back( *repl ); } } - /* check outputs */ - ntk.replace_in_outputs( _old, _new ); - - /* reset fan-in of old node */ - ntk.take_out_node( _old ); - } - - return updates; - } - - void update_vector( std::vector& vs, std::vector> const& updates ) - { - stopwatch t( st.time_update_vector ); + /* replace in outputs */ + ntk.replace_in_outputs( old_node, new_signal ); - for ( auto it = std::begin( vs ); it != std::end( vs ); ++it ) - { - for ( auto it2 = std::begin( updates ); it2 != std::end( updates ); ++it2 ) + /* replace in substitutions */ + for ( auto& s : substitutions ) { - if ( ntk.get_node( *it ) == it2->first ) + if ( ntk.get_node( s.second ) == old_node ) { - *it = ntk.is_complemented( *it ) ? !it2->second : it2->second; + s.second = ntk.is_complemented( s.second ) ? !new_signal : new_signal; + ntk.incr_fanout_size( ntk.get_node( new_signal ) ); } } + + /* finally remove the node: note that we never decrement the + fanout_size of the old_node. instead, we remove the node and + reset its fanout_size to 0 knowing that it must be 0 after + substituting all references. */ + assert( !ntk.is_dead( old_node ) ); + ntk.take_out_node( old_node ); + + /* decrement fanout_size when released from substitution list */ + ntk.decr_fanout_size( ntk.get_node( new_signal ) ); } + + ntk._events->on_delete.pop_back(); } /* recursively update the node levels and the depth of the critical path */ @@ -424,21 +463,35 @@ 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 ); - ntk.foreach_fanout( n, [&]( const auto& p ) { - update_node_level( p ); + if ( !ntk.is_dead( p ) ) + { + update_node_level( p ); + } } ); } } + void update_depth() + { + uint32_t max_level{0}; + ntk.foreach_co( [&]( signal s ){ + assert( !ntk.is_dead( ntk.get_node( s ) ) ); + if ( ntk.level( ntk.get_node( s ) ) > max_level ) + { + max_level = ntk.level( ntk.get_node( s ) ); + } + }); + + if ( ntk.depth() != max_level ) + { + ntk.set_depth( max_level ); + } + } + private: Ntk& ntk; window_rewriting_params ps; From b7e840fe0cb7fc84998a25223e54c0a530df9637 Mon Sep 17 00:00:00 2001 From: Heinz Riener Date: Fri, 23 Apr 2021 11:15:37 +0200 Subject: [PATCH 02/10] consider time of events. --- include/mockturtle/algorithms/window_rewriting.hpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/include/mockturtle/algorithms/window_rewriting.hpp b/include/mockturtle/algorithms/window_rewriting.hpp index da6d2743b..6e81b6970 100644 --- a/include/mockturtle/algorithms/window_rewriting.hpp +++ b/include/mockturtle/algorithms/window_rewriting.hpp @@ -179,17 +179,23 @@ class window_rewriting_impl , st( st ) { auto const update_level_of_new_node = [&]( const auto& n ) { + stopwatch t( st.time_total ); + ntk.resize_levels(); update_node_level( n ); }; auto const update_level_of_existing_node = [&]( node const& n, const auto& old_children ) { + stopwatch t( st.time_total ); + (void)old_children; ntk.resize_levels(); update_node_level( n ); }; auto const update_level_of_deleted_node = [&]( node const& n ) { + stopwatch t( st.time_total ); + assert( ntk.fanout_size( n ) == 0u ); ntk.set_level( n, -1 ); }; From 3d20f7d3a2cb1a2f98dfefe8739deb8f66c9fcd6 Mon Sep 17 00:00:00 2001 From: Heinz Riener Date: Fri, 23 Apr 2021 11:18:05 +0200 Subject: [PATCH 03/10] minor cleanup. --- include/mockturtle/algorithms/window_rewriting.hpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/include/mockturtle/algorithms/window_rewriting.hpp b/include/mockturtle/algorithms/window_rewriting.hpp index 6e81b6970..aff46573e 100644 --- a/include/mockturtle/algorithms/window_rewriting.hpp +++ b/include/mockturtle/algorithms/window_rewriting.hpp @@ -376,7 +376,7 @@ class window_rewriting_impl { ntk.decr_fanout_size( nn ); } - /* remove the node if it's fanout_size becomes 0 */ + /* remove the node if its fanout_size becomes 0 */ if ( ntk.fanout_size( nn ) == 0 ) { ntk.take_out_node( nn ); @@ -406,8 +406,7 @@ class window_rewriting_impl substitutions.pop_front(); // for ( auto index = 1u; index < _storage->nodes.size(); ++index ) - const auto parents = ntk.fanout( old_node ); - for ( auto index : parents ) + for ( auto index : ntk.fanout( old_node ) ) { /* skip CIs and dead nodes */ if ( ntk.is_dead( index ) ) From 520c2dd679115675d50a0be104809fd5257f72a9 Mon Sep 17 00:00:00 2001 From: Heinz Riener Date: Fri, 23 Apr 2021 11:20:30 +0200 Subject: [PATCH 04/10] if a cyclic substitution is detected, avoid substitution (for all outputs). --- include/mockturtle/algorithms/window_rewriting.hpp | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/include/mockturtle/algorithms/window_rewriting.hpp b/include/mockturtle/algorithms/window_rewriting.hpp index aff46573e..e0dd3f946 100644 --- a/include/mockturtle/algorithms/window_rewriting.hpp +++ b/include/mockturtle/algorithms/window_rewriting.hpp @@ -269,10 +269,15 @@ 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; - if ( ntk.fanout_size( ntk.get_node( _new ) ) == 0u ) + substitutions.emplace_back( std::make_pair( ntk.get_node( _old ), ntk.is_complemented( _old ) ? !_new : _new ) ); + for ( auto it = std::rbegin( substitutions ); it != std::rend( substitutions ); ++it ) { - ntk.take_out_node( ntk.get_node( _new ) ); + if ( ntk.fanout_size( ntk.get_node( it->second ) ) == 0u ) + { + ntk.take_out_node( ntk.get_node( it->second ) ); + } } + substitutions.clear(); return false; } From e1649eb70a1a89cd7119df613aa275d0738db19e Mon Sep 17 00:00:00 2001 From: Heinz Riener Date: Fri, 23 Apr 2021 11:22:31 +0200 Subject: [PATCH 05/10] takeout node if decremented fanout_size reaches 0. --- include/mockturtle/algorithms/window_rewriting.hpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/include/mockturtle/algorithms/window_rewriting.hpp b/include/mockturtle/algorithms/window_rewriting.hpp index e0dd3f946..ecbd4fa9d 100644 --- a/include/mockturtle/algorithms/window_rewriting.hpp +++ b/include/mockturtle/algorithms/window_rewriting.hpp @@ -452,6 +452,10 @@ class window_rewriting_impl /* decrement fanout_size when released from substitution list */ ntk.decr_fanout_size( ntk.get_node( new_signal ) ); + if ( ntk.fanout_size( ntk.get_node( new_signal ) ) == 0 ) + { + ntk.take_out_node( ntk.get_node( new_signal ) ); + } } ntk._events->on_delete.pop_back(); From f01228ef8725d19c022f4efe6b71a42529d3b1e0 Mon Sep 17 00:00:00 2001 From: Heinz Riener Date: Fri, 23 Apr 2021 11:23:02 +0200 Subject: [PATCH 06/10] count number of restrashings. --- include/mockturtle/algorithms/window_rewriting.hpp | 1 + 1 file changed, 1 insertion(+) diff --git a/include/mockturtle/algorithms/window_rewriting.hpp b/include/mockturtle/algorithms/window_rewriting.hpp index ecbd4fa9d..23e23f2e7 100644 --- a/include/mockturtle/algorithms/window_rewriting.hpp +++ b/include/mockturtle/algorithms/window_rewriting.hpp @@ -427,6 +427,7 @@ class window_rewriting_impl { ntk.incr_fanout_size( ntk.get_node( repl->second ) ); substitutions.emplace_back( *repl ); + ++st.num_restrashes; } } From 72c889b7a728b09e844e0d5353912500d3688d4a Mon Sep 17 00:00:00 2001 From: Heinz Riener Date: Fri, 23 Apr 2021 11:44:51 +0200 Subject: [PATCH 07/10] copy num_restrashes. --- include/mockturtle/algorithms/window_rewriting.hpp | 1 + 1 file changed, 1 insertion(+) diff --git a/include/mockturtle/algorithms/window_rewriting.hpp b/include/mockturtle/algorithms/window_rewriting.hpp index 23e23f2e7..67003d90a 100644 --- a/include/mockturtle/algorithms/window_rewriting.hpp +++ b/include/mockturtle/algorithms/window_rewriting.hpp @@ -93,6 +93,7 @@ struct window_rewriting_stats time_topo_sort += other.time_topo_sort; time_encode += other.time_encode; num_substitutions += other.num_substitutions; + num_restrashes += other.num_restrashes; num_windows += other.num_windows; gain += other.gain; return *this; From 52c74e8079d4f75606c8e1715c62ec3fc3c0a7ea Mon Sep 17 00:00:00 2001 From: Heinz Riener Date: Fri, 23 Apr 2021 16:21:15 +0200 Subject: [PATCH 08/10] window_rewriting. --- .../algorithms/window_rewriting.hpp | 165 +++++++++++++++--- include/mockturtle/utils/debugging_utils.hpp | 138 +++++++++++++++ include/mockturtle/utils/window_utils.hpp | 27 ++- 3 files changed, 302 insertions(+), 28 deletions(-) diff --git a/include/mockturtle/algorithms/window_rewriting.hpp b/include/mockturtle/algorithms/window_rewriting.hpp index 86d19d10b..a24af4fad 100644 --- a/include/mockturtle/algorithms/window_rewriting.hpp +++ b/include/mockturtle/algorithms/window_rewriting.hpp @@ -51,6 +51,27 @@ struct window_rewriting_params uint64_t cut_size{6}; uint64_t num_levels{5}; + /* Level information guides the windowing construction and as such impacts QoR: + -- dont_update: fastest, but levels are wrong (QoR degrades) + -- eager: fast, some levels are wrong + -- precise: fast, all levels are correct (best QoR) + -- recompute: slow, same as precise (used only for debugging) + */ + enum + { + /* do not update any levels */ + dont_update, + /* eagerly update the levels of changed nodes but avoid + topological sorting (some levels will be wrong) */ + eager, + /* precisely update the levels of changed nodes bottom-to-top and + in topological order */ + precise, + /* recompute all levels (also precise, but more expensive to + compute) */ + recompute, + } level_update_strategy = dont_update; + bool filter_cyclic_substitutions{false}; }; /* window_rewriting_params */ @@ -77,6 +98,9 @@ struct window_rewriting_stats /*! \brief Time for encoding index_list. */ stopwatch<>::duration time_encode{0}; + /*! \brief Time for detecting cycles. */ + stopwatch<>::duration time_cycle{0}; + /*! \brief Total number of calls to the resub. engine. */ uint64_t num_substitutions{0}; uint64_t num_restrashes{0}; @@ -178,26 +202,24 @@ class window_rewriting_impl : ntk( ntk ) , ps( ps ) , st( st ) + /* initialize levels to network depth */ + , levels( ntk.depth() ) { auto const update_level_of_new_node = [&]( const auto& n ) { stopwatch t( st.time_total ); - - ntk.resize_levels(); - update_node_level( n ); + update_levels( n ); }; auto const update_level_of_existing_node = [&]( node const& n, const auto& old_children ) { - stopwatch t( st.time_total ); - (void)old_children; - ntk.resize_levels(); - update_node_level( n ); + stopwatch t( st.time_total ); + update_levels( n ); }; auto const update_level_of_deleted_node = [&]( node const& n ) { stopwatch t( st.time_total ); - assert( ntk.fanout_size( n ) == 0u ); + assert( ntk.is_dead( n ) ); ntk.set_level( n, -1 ); }; @@ -212,7 +234,7 @@ class window_rewriting_impl create_window_impl windowing( ntk ); uint32_t const size = ntk.size(); - for ( uint32_t n = 0u; n < std::min( size, ntk.size() ); ++n ) + for ( uint32_t n = 0u; n < size; ++n ) { if ( ntk.is_constant( n ) || ntk.is_ci( n ) || ntk.is_dead( n ) ) { @@ -254,6 +276,9 @@ class window_rewriting_impl uint32_t counter{0}; ++st.num_substitutions; + /* ensure that no dead nodes are reachable */ + assert( count_reachable_dead_nodes( ntk ) == 0u ); + std::list> substitutions; insert( ntk, std::begin( signals ), std::end( signals ), *il_opt, [&]( signal const& _new ) @@ -267,7 +292,8 @@ 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 ( ps.filter_cyclic_substitutions && is_contained_in_tfi( ntk, ntk.get_node( _new ), ntk.get_node( _old ) ) ) + if ( ps.filter_cyclic_substitutions && + call_with_stopwatch( st.time_window, [&](){ return is_contained_in_tfi( ntk, ntk.get_node( _new ), ntk.get_node( _old ) ); }) ) { std::cout << "undo resubstitution " << ntk.get_node( _old ) << std::endl; substitutions.emplace_back( std::make_pair( ntk.get_node( _old ), ntk.is_complemented( _old ) ? !_new : _new ) ); @@ -286,11 +312,19 @@ class window_rewriting_impl return true; }); + /* ensure that no dead nodes are reachable */ + assert( count_reachable_dead_nodes( ntk ) == 0u ); substitute_nodes( substitutions ); /* recompute levels and depth */ - // call_with_stopwatch( st.time_levels, [&]() { ntk.update_levels(); } ); - update_depth(); + if ( ps.level_update_strategy == window_rewriting_params::recompute ) + { + call_with_stopwatch( st.time_levels, [&]() { ntk.update_levels(); } ); + } + if ( ps.level_update_strategy != window_rewriting_params::dont_update ) + { + update_depth(); + } /* ensure that no dead nodes are reachable */ assert( count_reachable_dead_nodes( ntk ) == 0u ); @@ -298,8 +332,12 @@ class window_rewriting_impl /* 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 ) ); + if ( ps.level_update_strategy == window_rewriting_params::precise || + ps.level_update_strategy == window_rewriting_params::recompute ) + { + /* ensure that the levels and depth is correct */ + assert( check_network_levels( ntk ) ); + } /* update internal data structures in windowing */ windowing.resize( ntk.size() ); @@ -415,12 +453,16 @@ class window_rewriting_impl { /* skip CIs and dead nodes */ if ( ntk.is_dead( index ) ) + { continue; + } /* skip nodes that will be deleted */ if ( std::find_if( std::begin( substitutions ), std::end( substitutions ), [&index]( auto s ){ return s.first == index; } ) != std::end( substitutions ) ) + { continue; + } /* replace in node */ if ( const auto repl = ntk.replace_in_node( index, old_node, new_signal ); repl ) @@ -462,11 +504,82 @@ class window_rewriting_impl ntk._events->on_delete.pop_back(); } - /* recursively update the node levels and the depth of the critical path */ - void update_node_level( node const& n ) + void update_levels( node const& n ) { - uint32_t const curr_level = ntk.level( n ); + ntk.resize_levels(); + if ( ps.level_update_strategy == window_rewriting_params::precise ) + { + assert( count_reachable_dead_nodes_from_node( ntk, n ) == 0u ); + assert( count_nodes_with_dead_fanins( ntk, n ) == 0u ); + call_with_stopwatch( st.time_levels, [&]() { update_node_level_precise( n ); } ); + } + else if ( ps.level_update_strategy == window_rewriting_params::eager ) + { + assert( count_reachable_dead_nodes_from_node( ntk, n ) == 0u ); + assert( count_nodes_with_dead_fanins( ntk, n ) == 0u ); + call_with_stopwatch( st.time_levels, [&]() { update_node_level_eager( n ); } ); + } + + /* levels can be wrong until substitute_nodes has finished */ + // assert( check_network_levels( ntk ) ); + } + + /* precisely update node levels using an iterative topological sorting approach */ + void update_node_level_precise( node const& n ) + { + /* compute level of current node */ + uint32_t level_offset{0}; + ntk.foreach_fanin( n, [&]( signal const& fi ){ + level_offset = std::max( ntk.level( ntk.get_node( fi ) ), level_offset ); + }); + ++level_offset; + + /* add node into levels */ + levels[0].emplace_back( n ); + + for ( uint32_t level_index = 0u; level_index < levels.size(); ++level_index ) + { + if ( levels[level_index].empty() ) + continue; + + for ( uint32_t node_index = 0u; node_index < levels[level_index].size(); ++node_index ) + { + node const p = levels[level_index][node_index]; + + /* recompute level of this node */ + uint32_t lvl{0}; + ntk.foreach_fanin( p, [&]( signal const& fi ){ + lvl = std::max( ntk.level( ntk.get_node( fi ) ), lvl ); + }); + ++lvl; + /* update level and add fanouts to levels[.] if the recomputed + level is different from the current level */ + if ( lvl != ntk.level( p ) ) + { + ntk.set_level( p, lvl ); + ntk.foreach_fanout( p, [&]( node const& fo ){ + assert( std::max( ntk.level( fo ), lvl + 1 ) >= level_offset ); + uint32_t const pos = std::max( ntk.level( fo ), lvl + 1 ) - level_offset; + assert( pos >= 0u ); + assert( pos >= level_index ); + if ( levels.size() <= pos ) + { + levels.resize( std::max( uint32_t( levels.size() << 1 ), pos + 1 ) ); + } + levels[pos].emplace_back( fo ); + }); + } + } + } + levels.clear(); + } + + /* eagerly update the node levels without topologically sorting (may + stack-overflow if the network is deep)*/ + void update_node_level_eager( node const& n ) + { + uint32_t const curr_level = ntk.level( n ); uint32_t max_level = 0; ntk.foreach_fanin( n, [&]( const auto& f ) { auto const p = ntk.get_node( f ); @@ -484,21 +597,21 @@ class window_rewriting_impl ntk.foreach_fanout( n, [&]( const auto& p ) { if ( !ntk.is_dead( p ) ) { - update_node_level( p ); + update_node_level_eager( p ); } } ); } } + /* update network depth (needs level information!) */ void update_depth() { + stopwatch t( st.time_levels ); + uint32_t max_level{0}; - ntk.foreach_co( [&]( signal s ){ + ntk.foreach_co( [&]( signal const& s ){ assert( !ntk.is_dead( ntk.get_node( s ) ) ); - if ( ntk.level( ntk.get_node( s ) ) > max_level ) - { - max_level = ntk.level( ntk.get_node( s ) ); - } + max_level = std::max( ntk.level( ntk.get_node( s ) ), max_level ); }); if ( ntk.depth() != max_level ) @@ -511,9 +624,11 @@ class window_rewriting_impl Ntk& ntk; window_rewriting_params ps; window_rewriting_stats& st; -}; /* window_rewriting_impl */ -} /* detail */ + std::vector> levels; +}; + +} /* namespace detail */ template void window_rewriting( Ntk& ntk, window_rewriting_params const& ps = {}, window_rewriting_stats* pst = nullptr ) diff --git a/include/mockturtle/utils/debugging_utils.hpp b/include/mockturtle/utils/debugging_utils.hpp index 3137619da..9c0992f80 100644 --- a/include/mockturtle/utils/debugging_utils.hpp +++ b/include/mockturtle/utils/debugging_utils.hpp @@ -38,6 +38,35 @@ namespace mockturtle { +template +inline void print( Ntk const& ntk ) +{ + using node = typename Ntk::node; + using signal = typename Ntk::signal; + + for ( uint32_t n = 0; n < ntk.size(); ++n ) + { + std::cout << n; + + if ( ntk.is_constant( n ) || ntk.is_pi( n ) ) + { + std::cout << std::endl; + continue; + } + + std::cout << " = "; + + ntk.foreach_fanin( n, [&]( signal const& fi ){ + std::cout << ( ntk.is_complemented( fi ) ? "~" : "" ) << ntk.get_node( fi ) << " "; + }); + std::cout << " ; [level = " << int32_t( ntk.level( n ) ) << "]" << " [dead = " << ntk.is_dead( n ) << "]" << " [ref = " << ntk.fanout_size( n ) << "]" << std::endl; + } + + ntk.foreach_co( [&]( signal const& s ){ + std::cout << "o " << ( ntk.is_complemented( s ) ? "~" : "" ) << ntk.get_node( s ) << std::endl; + }); +} + template inline uint64_t count_dead_nodes( Ntk const& ntk ) { @@ -130,6 +159,115 @@ inline uint64_t count_reachable_dead_nodes( Ntk const& ntk ) namespace detail { +template +void count_reachable_dead_nodes_from_node_recur( Ntk const& ntk, typename Ntk::node const& n, std::vector& nodes ) +{ + using node = typename Ntk::node; + + if ( ntk.current_color() == ntk.color( n ) ) + { + return; + } + + if ( ntk.is_dead( n ) ) + { + if ( std::find( std::begin( nodes ), std::end( nodes ), n ) == std::end( nodes ) ) + { + nodes.push_back( n ); + } + } + + ntk.paint( n ); + ntk.foreach_fanout( n, [&]( node const& fo ){ + count_reachable_dead_nodes_from_node_recur( ntk, fo, nodes ); + }); +} + +} /* namespace detail */ + +template +inline uint64_t count_reachable_dead_nodes_from_node( Ntk const& ntk, typename Ntk::node const& n ) +{ + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_color_v, "Ntk does not implement the color function" ); + static_assert( has_current_color_v, "Ntk does not implement the current_color function" ); + static_assert( has_foreach_co_v, "Ntk does not implement the foreach_co function" ); + static_assert( has_foreach_fanin_v, "Ntk does not implement the foreach_fanin function" ); + static_assert( has_get_node_v, "Ntk does not implement the get_node function" ); + // static_assert( has_is_dead_v, "Ntk does not implement the is_dead function" ); + static_assert( has_new_color_v, "Ntk does not implement the new_color function" ); + static_assert( has_paint_v, "Ntk does not implement the paint function" ); + + using node = typename Ntk::node; + using signal = typename Ntk::signal; + + ntk.new_color(); + + std::vector dead_nodes; + detail::count_reachable_dead_nodes_from_node_recur( ntk, n, dead_nodes ); + + return dead_nodes.size(); +} + +namespace detail +{ + +template +void count_nodes_with_dead_fanins_recur( Ntk const& ntk, typename Ntk::node const& n, std::vector& nodes ) +{ + using node = typename Ntk::node; + using signal = typename Ntk::signal; + + if ( ntk.current_color() == ntk.color( n ) ) + { + return; + } + + ntk.foreach_fanin( n, [&]( signal const& s ){ + if ( ntk.is_dead( ntk.get_node( s ) ) ) + { + if ( std::find( std::begin( nodes ), std::end( nodes ), n ) == std::end( nodes ) ) + { + nodes.push_back( n ); + } + } + }); + + ntk.paint( n ); + ntk.foreach_fanout( n, [&]( node const& fo ){ + count_reachable_dead_nodes_from_node_recur( ntk, fo, nodes ); + }); +} + +} /* namespace detail */ + +template +inline uint64_t count_nodes_with_dead_fanins( Ntk const& ntk, typename Ntk::node const& n ) +{ + static_assert( is_network_type_v, "Ntk is not a network type" ); + static_assert( has_color_v, "Ntk does not implement the color function" ); + static_assert( has_current_color_v, "Ntk does not implement the current_color function" ); + static_assert( has_foreach_co_v, "Ntk does not implement the foreach_co function" ); + static_assert( has_foreach_fanin_v, "Ntk does not implement the foreach_fanin function" ); + static_assert( has_get_node_v, "Ntk does not implement the get_node function" ); + // static_assert( has_is_dead_v, "Ntk does not implement the is_dead function" ); + static_assert( has_new_color_v, "Ntk does not implement the new_color function" ); + static_assert( has_paint_v, "Ntk does not implement the paint function" ); + + using node = typename Ntk::node; + using signal = typename Ntk::signal; + + ntk.new_color(); + + std::vector nodes_with_dead_fanins; + detail::count_nodes_with_dead_fanins_recur( ntk, n, nodes_with_dead_fanins ); + + return nodes_with_dead_fanins.size(); +} + +namespace detail +{ + template bool network_is_acylic_recur( Ntk const& ntk, typename Ntk::node const& n ) { diff --git a/include/mockturtle/utils/window_utils.hpp b/include/mockturtle/utils/window_utils.hpp index 4e7537111..2d08a6416 100644 --- a/include/mockturtle/utils/window_utils.hpp +++ b/include/mockturtle/utils/window_utils.hpp @@ -664,7 +664,7 @@ void expand_towards_tfo( Ntk const& ntk, std::vector const& namespace detail { -template +template void levelized_expand_towards_tfo( Ntk const& ntk, std::vector const& inputs, std::vector& nodes, std::vector>& levels ) { @@ -685,6 +685,13 @@ void levelized_expand_towards_tfo( Ntk const& ntk, std::vector +template void levelized_expand_towards_tfo( Ntk const& ntk, std::vector const& inputs, std::vector& nodes ) { std::vector> levels; - detail::levelized_expand_towards_tfo( ntk, inputs, nodes, levels ); + detail::levelized_expand_towards_tfo( ntk, inputs, nodes, levels ); } namespace detail From cbbe628f8a30a24549a0283f7eb38d909cadc230 Mon Sep 17 00:00:00 2001 From: Heinz Riener Date: Fri, 23 Apr 2021 17:22:52 +0200 Subject: [PATCH 09/10] resize levels after each iteration. --- include/mockturtle/algorithms/window_rewriting.hpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/include/mockturtle/algorithms/window_rewriting.hpp b/include/mockturtle/algorithms/window_rewriting.hpp index a24af4fad..748621a66 100644 --- a/include/mockturtle/algorithms/window_rewriting.hpp +++ b/include/mockturtle/algorithms/window_rewriting.hpp @@ -571,8 +571,12 @@ class window_rewriting_impl }); } } + + /* clean the level */ + levels[level_index].clear(); } levels.clear(); + levels.resize( ntk.depth() ); } /* eagerly update the node levels without topologically sorting (may From 343ccfb4f4e2ce7535ecd425de0ce42a92accb80 Mon Sep 17 00:00:00 2001 From: Heinz Riener Date: Fri, 23 Apr 2021 18:55:32 +0200 Subject: [PATCH 10/10] window_rewriting. --- .../mockturtle/algorithms/window_rewriting.hpp | 17 ++++++++++++----- include/mockturtle/utils/debugging_utils.hpp | 7 +++---- 2 files changed, 15 insertions(+), 9 deletions(-) diff --git a/include/mockturtle/algorithms/window_rewriting.hpp b/include/mockturtle/algorithms/window_rewriting.hpp index 748621a66..95dfe1b78 100644 --- a/include/mockturtle/algorithms/window_rewriting.hpp +++ b/include/mockturtle/algorithms/window_rewriting.hpp @@ -509,14 +509,10 @@ class window_rewriting_impl ntk.resize_levels(); if ( ps.level_update_strategy == window_rewriting_params::precise ) { - assert( count_reachable_dead_nodes_from_node( ntk, n ) == 0u ); - assert( count_nodes_with_dead_fanins( ntk, n ) == 0u ); call_with_stopwatch( st.time_levels, [&]() { update_node_level_precise( n ); } ); } else if ( ps.level_update_strategy == window_rewriting_params::eager ) { - assert( count_reachable_dead_nodes_from_node( ntk, n ) == 0u ); - assert( count_nodes_with_dead_fanins( ntk, n ) == 0u ); call_with_stopwatch( st.time_levels, [&]() { update_node_level_eager( n ); } ); } @@ -527,6 +523,9 @@ class window_rewriting_impl /* precisely update node levels using an iterative topological sorting approach */ void update_node_level_precise( node const& n ) { + assert( count_reachable_dead_nodes_from_node( ntk, n ) == 0u ); + // assert( count_nodes_with_dead_fanins( ntk, n ) == 0u ); + /* compute level of current node */ uint32_t level_offset{0}; ntk.foreach_fanin( n, [&]( signal const& fi ){ @@ -535,6 +534,10 @@ class window_rewriting_impl ++level_offset; /* add node into levels */ + if ( levels.size() < 1u ) + { + levels.resize( 1u ); + } levels[0].emplace_back( n ); for ( uint32_t level_index = 0u; level_index < levels.size(); ++level_index ) @@ -549,9 +552,14 @@ class window_rewriting_impl /* recompute level of this node */ uint32_t lvl{0}; ntk.foreach_fanin( p, [&]( signal const& fi ){ + if ( ntk.is_dead( ntk.get_node( fi ) ) ) + return; + lvl = std::max( ntk.level( ntk.get_node( fi ) ), lvl ); + return; }); ++lvl; + assert( lvl > 0 ); /* update level and add fanouts to levels[.] if the recomputed level is different from the current level */ @@ -576,7 +584,6 @@ class window_rewriting_impl levels[level_index].clear(); } levels.clear(); - levels.resize( ntk.depth() ); } /* eagerly update the node levels without topologically sorting (may diff --git a/include/mockturtle/utils/debugging_utils.hpp b/include/mockturtle/utils/debugging_utils.hpp index 9c0992f80..8639a4e2d 100644 --- a/include/mockturtle/utils/debugging_utils.hpp +++ b/include/mockturtle/utils/debugging_utils.hpp @@ -222,6 +222,7 @@ void count_nodes_with_dead_fanins_recur( Ntk const& ntk, typename Ntk::node cons { return; } + ntk.paint( n ); ntk.foreach_fanin( n, [&]( signal const& s ){ if ( ntk.is_dead( ntk.get_node( s ) ) ) @@ -233,16 +234,15 @@ void count_nodes_with_dead_fanins_recur( Ntk const& ntk, typename Ntk::node cons } }); - ntk.paint( n ); ntk.foreach_fanout( n, [&]( node const& fo ){ - count_reachable_dead_nodes_from_node_recur( ntk, fo, nodes ); + count_nodes_with_dead_fanins_recur( ntk, fo, nodes ); }); } } /* namespace detail */ template -inline uint64_t count_nodes_with_dead_fanins( Ntk const& ntk, typename Ntk::node const& n ) +uint64_t count_nodes_with_dead_fanins( Ntk const& ntk, typename Ntk::node const& n ) { static_assert( is_network_type_v, "Ntk is not a network type" ); static_assert( has_color_v, "Ntk does not implement the color function" ); @@ -255,7 +255,6 @@ inline uint64_t count_nodes_with_dead_fanins( Ntk const& ntk, typename Ntk::node static_assert( has_paint_v, "Ntk does not implement the paint function" ); using node = typename Ntk::node; - using signal = typename Ntk::signal; ntk.new_color();