https://bugs.kde.org/show_bug.cgi?id=524780

--- Comment #7 from [email protected] ---
Thanks a lot for looking into this, Ralf. Your QTextBrowser measurements are
right:
the renderer really does cost noticeable time, and my original report was wrong
to
call it "ruled out". On my machine, rendering my exported report HTML takes
about
1.5 s in a plain QTextBrowser (Qt 5.15 and Qt 6.4 alike).

But that does not explain the 50 s I see, so I tried again with the help of
Claude
(an AI assistant). This time we found the cause and could reproduce it in
isolation.
I can't judge the C++ details myself, so please check it.

**What I tested**

- Nightly Flatpak (master, 2026-10-13, Qt 6.11) with my *anonymized file
attached to
  this bug*, report R000183: about **60 s** on my machine (i7-1360P).
  So with the same code and the same file you get ~8 s and I get ~60 s.
  The data is not the difference, the environment is.

- Profiled that run with the org.kde.kmymoney.Debug symbols (gdb sampling,
  197 non-idle samples, build IDs verified):

  ```
  100.0%  KReportTab::showEvent
   93.4%  reports::PivotTable::renderHTML
   51.8%  QArrayDataPointer<reports::PivotCell>::reallocateAndGrow   (inside
renderHTML)
   43.1%  QArrayDataPointer<reports::PivotCell>::~QArrayDataPointer  (inside
renderHTML)
    6.6%  reports::PivotTable::init
  ```

  So almost all of the time is spent *building the HTML string*, not in the
report
  calculation and not in QTextBrowser. This also explains why the HTML export
  is just as slow for me.

**The cause (as far as we can tell): Qt5 → Qt6 change of `QMap::operator[]
const`**

`renderHTML()` is const and walks the grid with const_iterators. For every cell
it
reads, twice:

```cpp
it_row.value()[rowType][column]          // pivottable.cpp, renderHTML, e.g.
lines 1944 / 1946
```

- Qt5: `const T QMap::operator[](const Key&) const` returns a *const* copy, a
shallow,
  refcounted one. `[column]` then uses the const `QList::operator[]`. Nothing
is copied.
- Qt6: `T QMap::operator[](const Key&) const` returns a *non-const* copy.
  `[column]` then calls the non-const `QList::operator[]`, which **detaches**,
  because the data is still shared with the grid. So the *whole row*
  (all ~871 PivotCells) is deep-copied to read a single cell, and then thrown
away.

For my report that is roughly 31 rows × 871 columns × 2 accesses × 871 copied
cells
≈ 47 million PivotCell copies. The same pattern is used for the
inner/outer/grand
totals rows in renderHTML, and also in renderCSV.

**Isolated reproduction** (same nested QMap/QList types, same access pattern,
31 × 871 cells, only the Qt version differs):

| | access as in renderHTML | row taken once as const reference |
|---|---|---|
| Qt 5.15 | 2 ms | 0 ms |
| Qt 6.4 | **1465 ms** | 0 ms |

The real code is slower than this mock, because every PivotCell copy goes
through
AlkValue copy/destroy calls, but the profile signature is identical.

**Possible reason why you don't see it:** are you building against Qt5 on
openSUSE
Leap? With Qt5 this pattern costs nothing, which would leave only the
QTextBrowser
time you measured (~8 s). The Flatpak is Qt6. KMyMoney 5.1.3 (Qt5) is fast for
me.

**Possible fix direction:** in the const render methods, take a const reference
to
the row once instead of indexing through the temporary returned by
`QMap::operator[] const`, e.g.

```cpp
const PivotGridRow& row = *it_row.value().constFind(rowType);
... row[column] ...
```

(or a small const accessor on PivotGridRowSet that returns `const
PivotGridRow&`).
In the mock this brings Qt6 back to 0 ms.

The QTextBrowser cost you found would be a separate, second issue on top of
this.

-- 
You are receiving this mail because:
You are watching all bug changes.

Reply via email to