Page MenuHomePhabricator

Deleting (or cutting) entire page contents in NWE is very slow on pages with many lines
Open, LowPublic

Description

Deleting (or cutting) entire page contents in NWE is very slow on pages with many lines. Similarly long articles that have fewer lines are much faster,

Steps to reproduce: open long page, Ctrl+A, Delete.

Event Timeline

Restricted Application added a subscriber: Aklapper. · View Herald Transcript

I investigated this a bit and this is caused by TreeModifier doing one ve.batchSplice on the linear data per node in the document. Since wikitext documents are represented as one paragraph node per line of text, that ends up being a lot of nodes. So this is another thing that is accidentally quadratic in the worst case.

I think ideally TreeModifier should do just one batchSplice per transaction operation, but I have no idea if this is actually possible.

Failing that, we could use some different data structure for linear data that has faster splices than an array, e.g. a rope (or even something simpler, like splitting the huge array into multiple smaller arrays). This is definitely possible and probably a good idea anyway, but seems like a lot of effort, and we won't know if it will be much faster (or if it will be a regression for some other case) until we try it. Has anybody ever thought about doing this?

We could also try representing wikitext documents as just one huge paragraph node with newlines… we'd still have this problem for some normal documents, but they would be rarer. I don't know what's the reason for the current representation.

Thanks, @matmarex ! The purpose of the TreeModifier is to perform the linear modifiations in an order such that the tree structure is at all times valid and the view can stay closely in sync with the linear model. Even so, there may be scope to reduce the number of splices in such an obvious case - I'll take a look.

We could also try representing wikitext documents as just one huge paragraph node with newlines… we'd still have this problem for some normal documents, but they would be rarer. I don't know what's the reason for the current representation.

This is a possibility. It was just easier at the time, and allowed us to do single line regexes more easily.

We could also try representing wikitext documents as just one huge paragraph node with newlines… we'd still have this problem for some normal documents, but they would be rarer. I don't know what's the reason for the current representation.

This is a possibility. It was just easier at the time, and allowed us to do single line regexes more easily.

An issue with this is that we fairly regularly re-render the contents of the current node (e.g. after programmatic changes). When it is a single paragraph that isn't an issue, but if the whole doc had to re-render that may be a problem.

Still reproducible in 2026. It's also still presumably the same root cause, since the timings more-closely track lines not overall characters:

ArticleCharactersLines (paragraph nodes)Ctrl+A then Delete
Cats147k1866~290 ms
COVID-19 pandemic378k899~242 ms

CPU profiling results:

  • ve.dm.LinearData.splice / ve.batchSplice ~152 ms (44%): splicing the whole linear-data array.
  • removeChild + appendChild + cloneNode ~71 ms (21%): TreeModifier reconciling the CE DOM tree.
  • ve.dm.TreeModifier.checkEqualData ~28 ms + OO.copy ~19 ms: tree-diff data compare/copy.
  • OO.EventEmitter.emit heavy inclusive (~142 ms).

Call path: SurfaceFragment.insertContent > removeContent > Surface.changeInternal > Document.commit > TransactionProcessor.process > TreeModifier.process.

(Since I was checking things anyway, I also confirmed that the same full-document edit in WikiEditor's <textarea> is ~1 ms, as-expected.)

Change #1315107 had a related patch set uploaded (by DLynch; author: DLynch):

[VisualEditor/VisualEditor@master] TransactionProcessor: add fast-path for large source-mode replaces

https://gerrit.wikimedia.org/r/1315107

Testing on that same Cats article, that patch get a full-body replace down from 290ms to 20ms.

Change #1315107 merged by jenkins-bot:

[VisualEditor/VisualEditor@master] TransactionProcessor: add fast-path for large source-mode replaces

https://gerrit.wikimedia.org/r/1315107

^ that patch has added an optimized path for source mode, where when it notices a transaction that consists solely of retains + a replace that covers at least half of the attached root it skips the treemodifier path entirely, splices the new data in, and rebuilds the subtree. Means we can skip all the per-node splices and repetitive event-emits, which was almost all of the CPU time taken.

Fun side-effect of the path taken: select-all+delete is now very fast, ctrl+z to undo that delete is unchanged in how slow it is.

This was conveniently simple to think about. @matmarex's suggestion of improving TreeModifier so that it generically optimizes splices/emits, and/or a more efficient data structure for lineardata, is probably the right thing to pursue for a better overall fix.

(The more generic fixes would also spill onto visual mode. The profiling I did suggested that the most expensive part in visual mode tended to be the actual layout of the more-complicated rendered CE, so it's less of a priority there.)