mirror of
				https://github.com/Z3Prover/z3
				synced 2025-10-31 11:42:28 +00:00 
			
		
		
		
	Fix the real O(n²) bottleneck with lazy hash table for constructor name lookups
Co-authored-by: NikolajBjorner <3085284+NikolajBjorner@users.noreply.github.com>
This commit is contained in:
		
							parent
							
								
									b518327650
								
							
						
					
					
						commit
						dfdc31ae3b
					
				
					 3 changed files with 25 additions and 70 deletions
				
			
		|  | @ -1076,15 +1076,14 @@ namespace datatype { | ||||||
|          |          | ||||||
|         sort * datatype = con->get_range(); |         sort * datatype = con->get_range(); | ||||||
|         def const& d = get_def(datatype); |         def const& d = get_def(datatype); | ||||||
|         for (constructor const* c : d) { |         // Use O(1) lookup instead of O(n) linear search
 | ||||||
|             if (c->name() == con->get_name()) { |         constructor* c = d.get_constructor_by_name(con->get_name()); | ||||||
|  |         if (c) { | ||||||
|             for (accessor const* a : *c) { |             for (accessor const* a : *c) { | ||||||
|                 func_decl_ref fn = a->instantiate(datatype); |                 func_decl_ref fn = a->instantiate(datatype); | ||||||
|                 res->push_back(fn); |                 res->push_back(fn); | ||||||
|                 plugin().add_ast(fn); |                 plugin().add_ast(fn); | ||||||
|             } |             } | ||||||
|                 break; |  | ||||||
|             } |  | ||||||
|         } |         } | ||||||
|         return res; |         return res; | ||||||
|     } |     } | ||||||
|  | @ -1105,13 +1104,11 @@ namespace datatype { | ||||||
|         sort * datatype = con->get_range(); |         sort * datatype = con->get_range(); | ||||||
|         def const& dd = get_def(datatype); |         def const& dd = get_def(datatype); | ||||||
|         symbol r; |         symbol r; | ||||||
|         // This should be fixed for perf.
 |         // Use O(1) lookup instead of O(n) linear search
 | ||||||
|         // Option 1: hash-table in dd that maps to constructors instead of iterating over all constructors.
 |         constructor* c = dd.get_constructor_by_name(con->get_name()); | ||||||
|         // initialize the hash-table lazily when dd is large.
 |         if (c) { | ||||||
|         // Option 2: initialize all calls to plugin() registration in a single pass.
 |  | ||||||
|         for (constructor const* c : dd)  |  | ||||||
|             if (c->name() == con->get_name())  |  | ||||||
|             r = c->recognizer(); |             r = c->recognizer(); | ||||||
|  |         } | ||||||
|         parameter ps[2] = { parameter(con), parameter(r) }; |         parameter ps[2] = { parameter(con), parameter(r) }; | ||||||
|         d  = m.mk_func_decl(fid(), OP_DT_RECOGNISER, 2, ps, 1, &datatype); |         d  = m.mk_func_decl(fid(), OP_DT_RECOGNISER, 2, ps, 1, &datatype); | ||||||
|         SASSERT(d); |         SASSERT(d); | ||||||
|  | @ -1121,56 +1118,6 @@ namespace datatype { | ||||||
|         return d; |         return d; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     void util::batch_initialize_constructor_functions(sort * datatype) { |  | ||||||
|         SASSERT(is_datatype(datatype)); |  | ||||||
|         def const& dd = get_def(datatype); |  | ||||||
|          |  | ||||||
|         // Get all constructors for this datatype
 |  | ||||||
|         ptr_vector<func_decl> const* constructors = get_datatype_constructors(datatype); |  | ||||||
|         if (!constructors) return; |  | ||||||
|          |  | ||||||
|         // Process all constructors in a single pass to avoid O(n²) behavior
 |  | ||||||
|         for (func_decl * con : *constructors) { |  | ||||||
|             // Initialize recognizer if not already cached
 |  | ||||||
|             if (!plugin().m_constructor2recognizer.contains(con)) { |  | ||||||
|                 symbol r; |  | ||||||
|                 for (constructor const* c : dd) { |  | ||||||
|                     if (c->name() == con->get_name()) { |  | ||||||
|                         r = c->recognizer(); |  | ||||||
|                         break; |  | ||||||
|                     } |  | ||||||
|                 } |  | ||||||
|                 parameter ps[2] = { parameter(con), parameter(r) }; |  | ||||||
|                 func_decl* d = m.mk_func_decl(fid(), OP_DT_RECOGNISER, 2, ps, 1, &datatype); |  | ||||||
|                 plugin().add_ast(con); |  | ||||||
|                 plugin().add_ast(d); |  | ||||||
|                 plugin().m_constructor2recognizer.insert(con, d); |  | ||||||
|             } |  | ||||||
|              |  | ||||||
|             // Initialize accessors if not already cached
 |  | ||||||
|             if (!plugin().m_constructor2accessors.contains(con)) { |  | ||||||
|                 ptr_vector<func_decl>* res = alloc(ptr_vector<func_decl>); |  | ||||||
|                 plugin().add_ast(con); |  | ||||||
|                 plugin().m_vectors.push_back(res); |  | ||||||
|                 plugin().m_constructor2accessors.insert(con, res); |  | ||||||
|                  |  | ||||||
|                 if (con->get_arity() > 0) { |  | ||||||
|                     // Find the constructor definition and create accessors
 |  | ||||||
|                     for (constructor const* c : dd) { |  | ||||||
|                         if (c->name() == con->get_name()) { |  | ||||||
|                             for (accessor const* a : *c) { |  | ||||||
|                                 func_decl_ref fn = a->instantiate(datatype); |  | ||||||
|                                 res->push_back(fn); |  | ||||||
|                                 plugin().add_ast(fn); |  | ||||||
|                             } |  | ||||||
|                             break; |  | ||||||
|                         } |  | ||||||
|                     } |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     app* util::mk_is(func_decl * c, expr *f) { |     app* util::mk_is(func_decl * c, expr *f) { | ||||||
|         return m.mk_app(get_constructor_is(c), f); |         return m.mk_app(get_constructor_is(c), f); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  | @ -26,6 +26,7 @@ Revision History: | ||||||
| #include "util/buffer.h" | #include "util/buffer.h" | ||||||
| #include "util/symbol_table.h" | #include "util/symbol_table.h" | ||||||
| #include "util/obj_hashtable.h" | #include "util/obj_hashtable.h" | ||||||
|  | #include "util/dictionary.h" | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| enum sort_kind { | enum sort_kind { | ||||||
|  | @ -164,6 +165,7 @@ namespace datatype { | ||||||
|         sort_ref_vector         m_params; |         sort_ref_vector         m_params; | ||||||
|         mutable sort_ref        m_sort; |         mutable sort_ref        m_sort; | ||||||
|         ptr_vector<constructor> m_constructors; |         ptr_vector<constructor> m_constructors; | ||||||
|  |         mutable dictionary<constructor*> m_name2constructor; | ||||||
|     public: |     public: | ||||||
|         def(ast_manager& m, util& u, symbol const& n, unsigned class_id, unsigned num_params, sort * const* params): |         def(ast_manager& m, util& u, symbol const& n, unsigned class_id, unsigned num_params, sort * const* params): | ||||||
|             m(m), |             m(m), | ||||||
|  | @ -195,6 +197,17 @@ namespace datatype { | ||||||
|         util& u() const { return m_util; } |         util& u() const { return m_util; } | ||||||
|         param_size::size* sort_size() { return m_sort_size; } |         param_size::size* sort_size() { return m_sort_size; } | ||||||
|         void set_sort_size(param_size::size* p) { auto* q = m_sort_size;  m_sort_size = p; if (p) p->inc_ref(); if (q) q->dec_ref(); m_sort = nullptr; } |         void set_sort_size(param_size::size* p) { auto* q = m_sort_size;  m_sort_size = p; if (p) p->inc_ref(); if (q) q->dec_ref(); m_sort = nullptr; } | ||||||
|  |         constructor* get_constructor_by_name(symbol const& name) const { | ||||||
|  |             // Lazy initialization of name-to-constructor map for O(1) lookups
 | ||||||
|  |             if (m_name2constructor.empty() && !m_constructors.empty()) { | ||||||
|  |                 for (constructor* c : m_constructors) { | ||||||
|  |                     m_name2constructor.insert(c->name(), c); | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |             constructor* result = nullptr; | ||||||
|  |             m_name2constructor.find(name, result); | ||||||
|  |             return result; | ||||||
|  |         } | ||||||
|         def* translate(ast_translation& tr, util& u); |         def* translate(ast_translation& tr, util& u); | ||||||
|     }; |     }; | ||||||
| 
 | 
 | ||||||
|  | @ -373,7 +386,6 @@ namespace datatype { | ||||||
|         func_decl * get_constructor_recognizer(func_decl * constructor); |         func_decl * get_constructor_recognizer(func_decl * constructor); | ||||||
|         func_decl * get_constructor_is(func_decl * constructor); |         func_decl * get_constructor_is(func_decl * constructor); | ||||||
|         ptr_vector<func_decl> const * get_constructor_accessors(func_decl * constructor); |         ptr_vector<func_decl> const * get_constructor_accessors(func_decl * constructor); | ||||||
|         void batch_initialize_constructor_functions(sort * datatype); |  | ||||||
|         func_decl * get_accessor_constructor(func_decl * accessor); |         func_decl * get_accessor_constructor(func_decl * accessor); | ||||||
|         func_decl * get_recognizer_constructor(func_decl * recognizer) const; |         func_decl * get_recognizer_constructor(func_decl * recognizer) const; | ||||||
|         func_decl * get_update_accessor(func_decl * update) const; |         func_decl * get_update_accessor(func_decl * update) const; | ||||||
|  |  | ||||||
|  | @ -2471,10 +2471,6 @@ cmd_context::dt_eh::dt_eh(cmd_context & owner): | ||||||
| 
 | 
 | ||||||
| void cmd_context::dt_eh::operator()(sort * dt, pdecl* pd) { | void cmd_context::dt_eh::operator()(sort * dt, pdecl* pd) { | ||||||
|     TRACE(new_dt_eh, tout << "new datatype: "; m_owner.pm().display(tout, dt); tout << "\n";); |     TRACE(new_dt_eh, tout << "new datatype: "; m_owner.pm().display(tout, dt); tout << "\n";); | ||||||
|      |  | ||||||
|     // Batch initialize all constructor functions to avoid O(n²) behavior for large datatypes  
 |  | ||||||
|     m_dt_util.batch_initialize_constructor_functions(dt); |  | ||||||
|      |  | ||||||
|     for (func_decl * c : *m_dt_util.get_datatype_constructors(dt)) { |     for (func_decl * c : *m_dt_util.get_datatype_constructors(dt)) { | ||||||
|         TRACE(new_dt_eh, tout << "new constructor: " << c->get_name() << "\n";); |         TRACE(new_dt_eh, tout << "new constructor: " << c->get_name() << "\n";); | ||||||
|         m_owner.insert(c); |         m_owner.insert(c); | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue