Repository navigation
Only re-read changed producers when refreshing a playlist - #1354
Merged
ddennedy merged 2 commits intoOct 26, 2026
Merged
Conversation
Contributor
There was a problem hiding this comment.
🟡 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
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
force-pushed
the
perf-playlist-refresh-revision
branch
from
October 25, 2026 17:49
9a1b7ab to
04ddacf
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.


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.cppat Kdenlive master 20feddcfa3 plus the project save/open scenario from the companion Kdenlive MR), statically linked, with only libmlt swapped throughLD_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.