Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add cache for SampleBuffer #6703

Closed
wants to merge 19 commits into from

Conversation

sakertooth
Copy link
Contributor

@sakertooth sakertooth commented May 4, 2023

This PR adds a cache for the SampleBuffer class, giving us two main benefits:

  • Faster load times by loading a sample from disk once and after that, from memory.
  • Potentially large memory savings by sharing samples between different users from one single data source.

Entries are automatically added to a QFileSystemWatcher once added to the cache, and when a file changes, it will evict it from the cache.

@superpaik
Copy link
Contributor

Test result on windows 10/64b, both mscv and mingw, loading a wav file into sample track (values are approximate)

build master PR master PR master PR
filesize 1 min 1 min 4 min 4 min 16 min 16 min
msvc 2.58 s 2.6 s 11 s 10 s 38 s 46 s
mingw 1.86 s 1.8 s 7 s 7 s 27 s 30 s

On the other hand, reloading the file is practically immediate on the PR. This can be positive if intended or negative if memory is not released when deleting a sample clip.

@sakertooth
Copy link
Contributor Author

Test result on windows 10/64b, both mscv and mingw, loading a wav file into sample track (values are approximate)
build master PR master PR master PR
filesize 1 min 1 min 4 min 4 min 16 min 16 min
msvc 2.58 s 2.6 s 11 s 10 s 38 s 46 s
mingw 1.86 s 1.8 s 7 s 7 s 27 s 30 s

On the other hand, reloading the file is practically immediate on the PR. This can be positive if intended or negative if memory is not released when deleting a sample clip.

Yeah, the initial load of the samples should be similar to master, but subsequent loads of the same sample should be faster since it is likely to be in the cache.

@superpaik
Copy link
Contributor

Then is working perfectly!
Especially useful when open a project that uses the same loop multiple times.

@michaelgregorius michaelgregorius added the rework required Pull requests which must be reworked to make it able to merge or review label Jun 2, 2023
@sakertooth
Copy link
Contributor Author

Having an LRU cache was mostly a premature idea I had in mind. What I plan to do now is store the samples in an unbounded cache, and allowing the end user to manage the samples loaded into the project. Deleting a sample from the cache would then require it to be loaded from disk, and loading a sample already in the cache will be fast because it's already in memory.

@sakertooth sakertooth marked this pull request as draft June 3, 2023 04:35
@michaelgregorius
Copy link
Contributor

Hi @sakertooth,

if you manage all samples/audio regions in one place you should also consider to watch each of them using QFileSystemWatcher and reload them on a change. This way users could adjust the audio data in other applications and have the updated data directly available in LMMS. Classes which use that data will of course have to be aware that changes might occur, e.g. that an audio file might become shorter, and behave accordingly.

I think most DAWs, e.g. REAPER and Ardour, define regions (audio and MIDI) that can be placed along the project. These regions then only reference the data so that one audio file can then potentially be used by several regions. Changing the audio file, e.g. outside of the DAW, will result in all regions that reference it using the changed data.

If I understand correctly LMMS makes use of virtual memory because it loads audio data directly into memory. This way the operating system will take care of memory blocks (pages) that have not been used for a while by swapping them out. One could also consider to implement some more direct form of memory management which also does not immediately load all the data into memory. I am not really an expert on that topic but I can imagine that memory mapped files would be loaded lazily into memory as the blocks are accessed.

@sakertooth
Copy link
Contributor Author

if you manage all samples/audio regions in one place you should also consider to watch each of them using QFileSystemWatcher and reload them on a change. This way users could adjust the audio data in other applications and have the updated data directly available in LMMS.

Wouldn't this be considered a downside? If the user were to load a file and it was in the sample cache, and then move that same file into another directory, why should the DAW also move/remove the sample? I tested this behavior in other DAWs like FL, and it doesn't respond to file path changes at all. In FL, once you load a file from disk, it stays there. Deleting it from disk after the fact doesn't change anything about what FL previously loaded. And if the same sample was now in another directory and then loaded in, it would just be considered a new file altogether.

Unless you mean like a Refresh button of some kind, then that should be okay. However, I don't think this behavior should be automatic.

@michaelgregorius
Copy link
Contributor

My remark was not about the detection of removed/moved files but about the detection of changes within the files. The scenario is that a user keeps a file where it is but edits its content with an external audio editor. The basic premise is that an audio editor has more powerful tools to manipulate audio than a DAW. In REAPER I can change audio files with external editors and the changes are reflected immediately in the DAW. If a file is moved REAPER will show that the file is not available anymore.

I think it all boils down to how LMMS organizes/wants to organize large sets of audio data. If you want to keep the data safe in cases where the source file is moved to another location then this also implies that you either have to write a new file to a known location when the project is saved or you will have to include the audio data in the save file itself. The latter might not be a good idea with audio heavy projects and with regards to performance.

The latter question would also become relevant if audio recording is ever implemented in LMMS.

@luzpaz
Copy link
Contributor

luzpaz commented Jun 24, 2023

Any progress on this ?

@sakertooth sakertooth changed the title Add LRU cache for SampleBuffer Add cache for SampleBuffer Jul 17, 2023
@sakertooth
Copy link
Contributor Author

Any progress on this ?

I plan to continue with this PR soon.

@sakertooth sakertooth force-pushed the add-lru-samplecache branch from 979f0d0 to b731ee9 Compare July 20, 2023 18:16
@messmerd messmerd mentioned this pull request Oct 26, 2023
@sakertooth sakertooth removed the rework required Pull requests which must be reworked to make it able to merge or review label Dec 31, 2023
@sakertooth sakertooth marked this pull request as ready for review December 31, 2023 05:10
@sakertooth sakertooth changed the title Add cache for SampleBuffer Add cache for SampleBuffer Dec 31, 2023
src/core/SampleBufferCache.cpp Outdated Show resolved Hide resolved
src/core/SampleBufferCache.cpp Outdated Show resolved Hide resolved
Copy link
Member

@messmerd messmerd left a comment

Choose a reason for hiding this comment

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

Looks pretty good except for a few things

src/gui/SampleLoader.cpp Outdated Show resolved Hide resolved
src/core/SampleBufferCache.cpp Outdated Show resolved Hide resolved
src/core/SampleBufferCache.cpp Outdated Show resolved Hide resolved
src/core/SampleBufferCache.cpp Outdated Show resolved Hide resolved
@sakertooth sakertooth marked this pull request as draft January 3, 2024 04:18
@sakertooth sakertooth marked this pull request as ready for review January 3, 2024 04:39
src/core/SampleCache.cpp Outdated Show resolved Hide resolved
@Rossmaxx
Copy link
Contributor

Rossmaxx commented Jan 3, 2024

Good feature but I am having a few questions.

Where are the sample caches getting stored? In the disk or in memory?
If in disk, it would be better to have a samplecache folder within the user lmms folder where everything gets cached on loading and tweaks to project files accordingly.

Also, does this reduce duplication of samples?

@sakertooth
Copy link
Contributor Author

The buffers are cached in memory. Storing them on disk doesn't help at all because the file that we intend to cache after it has been loaded is on disk.

This act of just caching the results after we loaded the sample only helps with speeding up loading said sample into the project. Any memory saving improvements are only made possible because of the kind of data we are caching. We are caching shared_ptr's to these buffers, which then allows for the buffer to be shared with the cache.

What is really wanted is the sharing of these buffers. This cache only speeds up data retrieval by itself, but there are added RAM improvements only because of what we are caching and how anybody that needs a sample (think AFP, sample tracks, etc) can have some global place to go and get it if it's already been loaded.

This distinction I feel like hasn't been made and is now confusing this PR up by a lot.

@sakertooth
Copy link
Contributor Author

That being said, I have to go and fix this PR up.

@sakertooth sakertooth closed this Jan 5, 2024
@sakertooth sakertooth force-pushed the add-lru-samplecache branch from ecf1620 to 56e7f7d Compare January 5, 2024 23:49
@sakertooth sakertooth reopened this Jan 5, 2024
@messmerd messmerd mentioned this pull request Jan 11, 2024
@sakertooth
Copy link
Contributor Author

Closing in favor of #7058.

@sakertooth sakertooth closed this Jan 11, 2024
@sakertooth sakertooth deleted the add-lru-samplecache branch January 11, 2024 12:39
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.

9 participants