Skip to content

Only re-read changed producers when refreshing a playlist - #1354

Merged
ddennedy merged 2 commits into
mltframework:masterfrom
eseifert:perf-playlist-refresh-revision
Oct 26, 2026
Merged

ddennedy merged 2 commits into
mltframework:masterfrom
eseifert:perf-playlist-refresh-revision

Conversation

@eseifert

Copy link
Copy Markdown
Contributor

Every playlist edit ends with mlt_playlist_virtual_refresh(), which read the in and out points of every entry's producer through four locked property lookups, so editing or building a playlist of n clips cost O(n^2) lookups. With a few thousand clips on a track a single edit took milliseconds, and loading a project took minutes.

The first commit adds mlt_properties_revision(), a counter that changes whenever a property is set, cleared or renamed, and lets each entry remember its producer's revision, so the refresh only re-reads producers that changed (including changes made while events are blocked, which they always are for cuts). The second commit remembers whether an entry is a blank instead of doing three property lookups per entry in mlt_playlist_consolidate_blanks(), mlt_playlist_is_blank() and mlt_playlist_is_blank_at().

Micro-benchmark, 1000 clips separated by blanks on a track of a tractor: appending all clips 1.58 s down to 0.10 s, resizing a clip 1.8 ms down to 40 us, removing and re-inserting a clip as Kdenlive does when moving it 47.7 ms down to 0.8 ms.

Kdenlive's benchmark suite: opening a project of 4000 clips 29.9 s down to 1.74 s (17x), moving a group of 1000 clips on one track 62.1 s down to 1.21 s (52x), inserting 512 clips 8x faster. No scenario is slower outside the sample ranges (13 of 152 medians are lower, all with overlapping ranges).

Measured with Kdenlive's own benchmark suite (benchmark/timelinemodelbenchmark.cpp at Kdenlive master 20feddcfa3 plus the project save/open scenario from the companion Kdenlive MR), statically linked, with only libmlt swapped through LD_LIBRARY_PATH; the modules come from the same MLT build. Two complete runs of three samples each per build, interleaved; values are median ms per operation with min–max. RelWithDebInfo (-O2 -g), GCC, Qt 6.11.2, AMD Ryzen 7 4800H, off-screen, no other builds during the runs. Every correctness check of the suite passed in every run.

Possibly related reports of slow edits in long timelines (not verified to be caused by this): mltframework/shotcut#1031, and Kdenlive bugs https://bugs.kde.org/474564 and https://bugs.kde.org/475205.

@eseifert eseifert changed the title Perf playlist refresh revision Only re-read changed producers when refreshing a playlist Oct 25, 2026
@ddennedy
ddennedy requested a lite review from Copilot October 25, 2026 17:27

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Playlist entries can retain stale state when parent producers change or when producer blank status changes.

2 open issues
What changed in this PR

This pull request optimizes playlist editing and loading by avoiding repeated producer property lookups and caching blank-entry status.

Changes:

  • Adds atomic property revision tracking and exports mlt_properties_revision().
  • Refreshes playlist entries only when their producer state changes.
  • Caches blank status for playlist entries.
  • Adds regression tests for property revisions, refresh behavior, and blank handling.
File Description
src/​tests/​test_properties/​test_properties.cpp Tests property revision changes and read stability.
src/​tests/​test_playlist/​test_playlist.cpp Tests producer refresh detection and blank handling.
src/​framework/​mlt.vers Exports the new revision API symbol.
src/​framework/​mlt_properties.h Declares the public properties revision API.
src/​framework/​mlt_properties.c Implements revision tracking for property mutations and renames.
src/​framework/​mlt_playlist.c Uses revisions to skip unchanged refreshes and caches blank status.

Review effort: Lite

Comment thread src/framework/mlt_playlist.c Resolved
Comment thread src/framework/mlt_properties.h Resolved
mlt_playlist_virtual_refresh() runs after every playlist edit and whenever a producer of the playlist changes. It read the in and out points of every entry's producer through four locked property lookups, so each edit cost O(n) lookups and building or editing a playlist of n clips cost O(n^2) lookups. With a few thousand clips on a track a single edit took milliseconds and loading a project took minutes.

Add mlt_properties_revision(), a counter that changes whenever a property is set, cleared or renamed, and let each entry remember the revision of its producer from the last refresh. The refresh still compares every entry with its producer when one of the producer's properties changed, including changes made while events are blocked, which cuts always have, so the result is the same as before.

In a RelWithDebInfo build with 1000 clips separated by blanks on a track of a tractor: appending all clips went from 1.58 s to 0.10 s, resizing a clip from 1.8 ms to 40 us, a change of the parent producer from 1.8 ms to 34 us, and removing and inserting a clip as Kdenlive does when moving it from 47.7 ms to 5.0 ms.
mlt_playlist_consolidate_blanks() checked every entry with mlt_producer_is_blank(), which takes three property lookups through the cut parent, and mlt_playlist_is_blank() and mlt_playlist_is_blank_at() did the same for every call. Kdenlive consolidates the blanks after every insertion and removal and asks for blanks constantly, so this became the largest cost of an edit once the refresh was cheap.

The producer of an entry never changes, so determine whether it is a blank once when the entry is created. An entry whose producer was closed by autoclose still counts as a blank, like a NULL producer did before.

In a RelWithDebInfo build with 1000 clips separated by blanks on a track of a tractor, removing and inserting a clip as Kdenlive does when moving it went from 4.7 ms to 0.8 ms.
@eseifert
eseifert force-pushed the perf-playlist-refresh-revision branch from 9a1b7ab to 04ddacf Compare October 25, 2026 17:49
@ddennedy ddennedy added this to the v7.44.0 milestone Oct 26, 2026
@ddennedy
ddennedy merged commit d4ff2c0 into mltframework:master Oct 26, 2026
15 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants