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

Reduce memory consumption for soundpacks #25155

Merged
merged 4 commits into from Aug 27, 2018

Conversation

Projects
None yet
3 participants
@OrenAudeles
Copy link
Contributor

commented Aug 26, 2018

Summary

SUMMARY: Performance "Reduce memory consumption for soundpacks"

Purpose of change

Large soundpacks (see https://discourse.cataclysmdda.org/t/cdda-soundpack/15329 ) can inflate memory consumption by reloading the same sound effect resource multiple times.

Describe the solution

Track the paths and loaded sound resource Mix_Chunks to ensure each sound resource is loaded exactly once. When attempting to load a path first check to see if the path has already been loaded once. If the path has already been loaded, allocate a Mix_Chunk object as a non-allocating copy of the original resource data. If the path has not been loaded, attempt to load it. If successful store the pointer using the resource path, and return the result pointer.
Allocating a new Mix_Chunk as a non-allocating copy allows existing lifetime management of sound_effect objects to remain unchanged, Mix_FreeChunk will not attempt to deallocate the data pointer for non-allocating copies.

By doing this memory consumption is reduced, with reduction becoming more apparent with larger soundpacks with many reused sound resources.

Additional context

In the linked Soundpack there are 2607 sound effect variant paths loaded, 398 unique paths.
From debug.log instrumentation: Count[# of allocated resources] Bytes[# of bytes allocated total]
Existing Implementation: Sound Effects: Count[2607] Bytes[821,454,648]
PR's Implementation: Sound Effects: Count[398] Bytes[173,757,452]

@alanbrady
Copy link
Contributor

left a comment

I think I understand the gist what's being accomplished here and that's trying to avoid loading the same wav file multiple times for different sounds. Seems like a good idea, but this has problems I think. Seems good, more data to back it up wouldn't hurt.

@@ -2296,6 +2296,39 @@ void update_music_volume() {
}

#ifdef SDL_SOUND
std::unordered_map<std::string, Mix_Chunk*> unique_chunks;

This comment has been minimized.

Copy link
@alanbrady

alanbrady Aug 26, 2018

Contributor

I think this should be static

This comment has been minimized.

Copy link
@OrenAudeles

OrenAudeles Aug 26, 2018

Author Contributor

Sure, won't hurt to make it static. I don't think it's strictly necessary to do so (see other file-level global declarations within sdltiles.cpp) but I think it will prevent other files from externing it.

This comment has been minimized.

Copy link
@alanbrady

alanbrady Aug 26, 2018

Contributor

Yeah I mean it's actually a nitpick, should have labelled it so. Thanks!


(*nchunk) = *ref;
nchunk->allocated = 0;
return nchunk;

This comment has been minimized.

Copy link
@alanbrady

alanbrady Aug 26, 2018

Contributor

I think I'm confused here. You're doing an SDL_malloc to match up with the SDL_free, which sounds right, but then setting nchunk->allocated to 0 so it doesn't free it? I think one of these is wrong.

Now I think about it, doing the SDL_malloc still isn't saving any memory allocs, how is this saving memory? In your test instrumentation your only checking if allocated is null which you set it to. This isn't actually profiling memory, I would like to see some better instrumentation from a tool that was built to do this like valgrind.

This comment has been minimized.

Copy link
@alanbrady

alanbrady Aug 26, 2018

Contributor

Ah got it so the SDL_malloc is getting memory for the struct but the allocated field is telling it the abuf is already allocated by someone else, so don't free it with the struct. I understand now. Nevermind I think this is probably good. Some valgrind data to backup your instrumentation would still be nice.

This comment has been minimized.

Copy link
@OrenAudeles

OrenAudeles Aug 26, 2018

Author Contributor

Instrumentation was testing against allocated so that it didn't count the same memory region multiple times. I should have explained better, either in the PR description or the comments, how Mix_FreeChunk works wrt to allocated.

#endif

void load_soundset() {
#ifdef SDL_SOUND
unique_chunks.clear();

This comment has been minimized.

Copy link
@alanbrady

alanbrady Aug 26, 2018

Contributor

Don't we only need the one unique_chunks.clear() at the end?

This comment has been minimized.

Copy link
@OrenAudeles

OrenAudeles Aug 26, 2018

Author Contributor

Yes, will remove. I think I put that there then decided to put it at the end because it made more sense, then forgot to remove the first.

@alanbrady
Copy link
Contributor

left a comment

Thanks. Technically Mix_Chunk is a struct not object but that's bordering on pedantic.

@ZhilkinSerg ZhilkinSerg self-assigned this Aug 27, 2018

@ZhilkinSerg ZhilkinSerg merged commit b7cb44c into CleverRaven:master Aug 27, 2018

4 checks passed

continuous-integration/appveyor/pr AppVeyor build succeeded
Details
continuous-integration/travis-ci/pr The Travis CI build passed
Details
coverage/coveralls First build on sdl_sound_mem_reduction at 24.061%
Details
gorgon-ghprb Build finished.
Details

@ZhilkinSerg ZhilkinSerg removed their assignment Aug 27, 2018

@OrenAudeles OrenAudeles deleted the OrenAudeles:sdl_sound_mem_reduction branch Aug 27, 2018

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.