mirror of
https://github.com/Z3Prover/z3
synced 2025-07-29 07:27:57 +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,14 +1076,13 @@ namespace datatype {
|
|||
|
||||
sort * datatype = con->get_range();
|
||||
def const& d = get_def(datatype);
|
||||
for (constructor const* c : d) {
|
||||
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;
|
||||
// Use O(1) lookup instead of O(n) linear search
|
||||
constructor* c = d.get_constructor_by_name(con->get_name());
|
||||
if (c) {
|
||||
for (accessor const* a : *c) {
|
||||
func_decl_ref fn = a->instantiate(datatype);
|
||||
res->push_back(fn);
|
||||
plugin().add_ast(fn);
|
||||
}
|
||||
}
|
||||
return res;
|
||||
|
@ -1105,13 +1104,11 @@ namespace datatype {
|
|||
sort * datatype = con->get_range();
|
||||
def const& dd = get_def(datatype);
|
||||
symbol r;
|
||||
// This should be fixed for perf.
|
||||
// Option 1: hash-table in dd that maps to constructors instead of iterating over all constructors.
|
||||
// initialize the hash-table lazily when dd is large.
|
||||
// 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();
|
||||
// Use O(1) lookup instead of O(n) linear search
|
||||
constructor* c = dd.get_constructor_by_name(con->get_name());
|
||||
if (c) {
|
||||
r = c->recognizer();
|
||||
}
|
||||
parameter ps[2] = { parameter(con), parameter(r) };
|
||||
d = m.mk_func_decl(fid(), OP_DT_RECOGNISER, 2, ps, 1, &datatype);
|
||||
SASSERT(d);
|
||||
|
@ -1121,56 +1118,6 @@ namespace datatype {
|
|||
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) {
|
||||
return m.mk_app(get_constructor_is(c), f);
|
||||
}
|
||||
|
|
|
@ -26,6 +26,7 @@ Revision History:
|
|||
#include "util/buffer.h"
|
||||
#include "util/symbol_table.h"
|
||||
#include "util/obj_hashtable.h"
|
||||
#include "util/dictionary.h"
|
||||
|
||||
|
||||
enum sort_kind {
|
||||
|
@ -164,6 +165,7 @@ namespace datatype {
|
|||
sort_ref_vector m_params;
|
||||
mutable sort_ref m_sort;
|
||||
ptr_vector<constructor> m_constructors;
|
||||
mutable dictionary<constructor*> m_name2constructor;
|
||||
public:
|
||||
def(ast_manager& m, util& u, symbol const& n, unsigned class_id, unsigned num_params, sort * const* params):
|
||||
m(m),
|
||||
|
@ -195,6 +197,17 @@ namespace datatype {
|
|||
util& u() const { return m_util; }
|
||||
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; }
|
||||
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);
|
||||
};
|
||||
|
||||
|
@ -373,7 +386,6 @@ namespace datatype {
|
|||
func_decl * get_constructor_recognizer(func_decl * constructor);
|
||||
func_decl * get_constructor_is(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_recognizer_constructor(func_decl * recognizer) 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) {
|
||||
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)) {
|
||||
TRACE(new_dt_eh, tout << "new constructor: " << c->get_name() << "\n";);
|
||||
m_owner.insert(c);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue