qetproject: memoize repeated numbering title/id lookups per call

elementAutoNumIdToLink() queried numbering_scheme per symbol even when
many symbols on unbuilt folios share the same scheme; setUpFormula()
fetched the same scheme's rows twice (elementAutoNumCurrentFormula()
then elementAutoNum() separately). Both are real but didn't resolve
the tst_lazyfolios ASan timeout on their own -- see QETZ-DB-FIRST-PLAN.md's
2b-3 row for the remaining open question.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
ispyisail
2026-10-21 10:46:34 +13:00
parent 122678a797
commit e7d644003f
3 changed files with 49 additions and 16 deletions
+9 -10
View File
@@ -2568,21 +2568,20 @@ void Element::setUpFormula(bool code_letter, QUndoCommand *parent_undo)
if (diagram())
{
QString formula = diagram()
->project()
->elementAutoNumCurrentFormula();
QETProject *autonum_project = diagram()->project();
QString element_currentAutoNum = autonum_project->elementCurrentAutoNum();
//One query for the scheme's rows, read once and reused below --
//elementAutoNumCurrentFormula()+elementAutoNum() each re-fetched
//the same rows separately (2b-3: every numbering read is now a
//database query, so a second fetch is a second round trip).
NumerotationContext nc = autonum_project->elementAutoNum(element_currentAutoNum);
QString formula = autonum::numerotationContextToFormula(nc);
m_data.m_informations.addValue(QStringLiteral("formula"), formula);
storeInformation();
QString element_currentAutoNum = diagram()
->project()
->elementCurrentAutoNum();
const QUuid scheme_id = diagram()->project()->elementAutoNumId(element_currentAutoNum);
const QUuid scheme_id = autonum_project->elementAutoNumId(element_currentAutoNum);
setFormulaSchemeId(formula.isEmpty() ? QUuid() : scheme_id);
NumerotationContext nc = diagram()
->project()
->elementAutoNum(element_currentAutoNum);
//The number given is the first whose label no other element of
//the project carries: a counter set back, or an element which
+37 -5
View File
@@ -1886,13 +1886,16 @@ void QETProject::linkElementsToElementAutoNums()
void QETProject::linkElementsToElementAutoNums(Diagram *folio)
{
const QHash<QString, QStringList> titles_by_formula = elementAutoNumTitlesByFormula();
QHash<QUuid, QString> title_cache;
QHash<QString, QUuid> id_cache;
{
const auto items = folio->items();
for (QGraphicsItem *it : items) {
auto *el = qgraphicsitem_cast<Element *>(it);
if (!el) continue;
if (const auto id = elementAutoNumIdToLink(el->elementInformations(),
titles_by_formula)) {
titles_by_formula,
&title_cache, &id_cache)) {
el->setFormulaSchemeId(*id);
}
}
@@ -1922,7 +1925,9 @@ QHash<QString, QStringList> QETProject::elementAutoNumTitlesByFormula() const
*/
std::optional<QUuid> QETProject::elementAutoNumIdToLink(
const DiagramContext &information,
const QHash<QString, QStringList> &titles_by_formula) const
const QHash<QString, QStringList> &titles_by_formula,
QHash<QUuid, QString> *title_cache,
QHash<QString, QUuid> *id_cache) const
{
const QString formula = information.value(QETInformation::ELMT_FORMULA).toString();
const bool has_id = information.contains(QETInformation::ELMT_FORMULA_ID);
@@ -1932,14 +1937,36 @@ std::optional<QUuid> QETProject::elementAutoNumIdToLink(
if (has_id) return QUuid();
return std::nullopt;
}
if (!elementAutoNumTitle(id).isEmpty()) {
//Every symbol following the same scheme repeats the same id/title
//lookup; a project database query per symbol is the ASan-visible
//cost (F134-adjacent, 2b-3), so a caller walking many symbols may
//pass caches that live only for that one walk -- never persisted.
QString title;
if (title_cache && title_cache->contains(id)) {
title = title_cache->value(id);
} else {
title = elementAutoNumTitle(id);
if (title_cache) title_cache->insert(id, title);
}
if (!title.isEmpty()) {
return std::nullopt;
}
if (!has_id && !m_legacy_element_autonums) {
return std::nullopt; //A formula typed by hand
}
const QStringList matches = titles_by_formula.value(formula);
return matches.size() == 1 ? elementAutoNumId(matches.first()) : QUuid();
if (matches.size() != 1) {
return QUuid();
}
const QString &match = matches.first();
if (id_cache) {
auto it = id_cache->find(match);
if (it == id_cache->end()) {
it = id_cache->insert(match, elementAutoNumId(match));
}
return it.value();
}
return elementAutoNumId(match);
}
/**
@@ -1986,13 +2013,18 @@ QList<QETProject::SymbolNumbering> QETProject::symbolNumbering() const
for (Diagram *d : m_diagrams_list)
if (!d->isLoaded()) unbuilt_in_order << d->uuid();
const QHash<QString, QStringList> titles_by_formula = elementAutoNumTitlesByFormula();
//Every symbol on an unbuilt folio following the same scheme repeats
//the same title/id lookup; cache both directions for this one walk
//(never persisted -- a scheme rename between calls must see it).
QHash<QUuid, QString> title_cache;
QHash<QString, QUuid> id_cache;
for (const projectDataBase::FolioSymbol &row : m_data_base.symbolsOnFolios(unbuilt_in_order))
{
const DiagramContext info = m_data_base.elementInformation(row.uuid);
SymbolNumbering symbol;
symbol.label = info.value(QETInformation::ELMT_LABEL).toString();
symbol.formula = info.value(QETInformation::ELMT_FORMULA).toString();
const auto linked = elementAutoNumIdToLink(info, titles_by_formula);
const auto linked = elementAutoNumIdToLink(info, titles_by_formula, &title_cache, &id_cache);
symbol.scheme = linked ? *linked
: QUuid(info.value(QETInformation::ELMT_FORMULA_ID).toString());
symbol.sequence = row.sequence;
+3 -1
View File
@@ -537,7 +537,9 @@ class QETProject : public QObject
QHash<QString, QStringList> elementAutoNumTitlesByFormula() const;
std::optional<QUuid> elementAutoNumIdToLink(
const DiagramContext &information,
const QHash<QString, QStringList> &titles_by_formula) const;
const QHash<QString, QStringList> &titles_by_formula,
QHash<QUuid, QString> *title_cache = nullptr,
QHash<QString, QUuid> *id_cache = nullptr) const;
bool m_deferring_folios = false;
/// Opening with folios built only when needed: QET_LAZY_FOLIOS
/// is set and the database can be filled from the document