mirror of
https://github.com/Z3Prover/z3
synced 2025-08-06 19:21:22 +00:00
Fix O(n²) performance issue in CLI datatype declaration processing (#7712)
* Initial plan * Implement batch initialization fix for O(n²) datatype performance issue Co-authored-by: NikolajBjorner <3085284+NikolajBjorner@users.noreply.github.com> * Fix the real O(n²) bottleneck with lazy hash table for constructor name lookups Co-authored-by: NikolajBjorner <3085284+NikolajBjorner@users.noreply.github.com> * Optimize get_constructor_by_name: use func_decl* parameter, add linear search optimization for small datatypes, and ensure non-null postcondition Co-authored-by: NikolajBjorner <3085284+NikolajBjorner@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: NikolajBjorner <3085284+NikolajBjorner@users.noreply.github.com>
This commit is contained in:
parent
53c48f7226
commit
75678fc2c2
2 changed files with 39 additions and 15 deletions
|
@ -1076,14 +1076,13 @@ 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);
|
||||||
for (accessor const* a : *c) {
|
if (c) {
|
||||||
func_decl_ref fn = a->instantiate(datatype);
|
for (accessor const* a : *c) {
|
||||||
res->push_back(fn);
|
func_decl_ref fn = a->instantiate(datatype);
|
||||||
plugin().add_ast(fn);
|
res->push_back(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);
|
||||||
// initialize the hash-table lazily when dd is large.
|
if (c) {
|
||||||
// Option 2: initialize all calls to plugin() registration in a single pass.
|
r = c->recognizer();
|
||||||
for (constructor const* c : dd)
|
}
|
||||||
if (c->name() == con->get_name())
|
|
||||||
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);
|
||||||
|
|
|
@ -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,31 @@ 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(func_decl* con) const {
|
||||||
|
symbol const& name = con->get_name();
|
||||||
|
constructor* result = nullptr;
|
||||||
|
|
||||||
|
// For small datatypes (< 10 constructors), use linear search instead of hash table
|
||||||
|
if (m_constructors.size() < 10) {
|
||||||
|
for (constructor* c : m_constructors) {
|
||||||
|
if (c->name() == name) {
|
||||||
|
result = c;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
m_name2constructor.find(name, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
SASSERT(result); // Post-condition: get_constructor_by_name returns a non-null result
|
||||||
|
return result;
|
||||||
|
}
|
||||||
def* translate(ast_translation& tr, util& u);
|
def* translate(ast_translation& tr, util& u);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue