Server: avoid per-frame deep copy of audio buffers in CreateLevelsForAllConChannels#3803
Server: avoid per-frame deep copy of audio buffers in CreateLevelsForAllConChannels#3803mcfnord wants to merge 1 commit into
Conversation
CServer::CreateLevelsForAllConChannels() took its vecvecsData argument (CVector<CVector<int16_t>>, i.e. one audio buffer per connected client) by value. The function is called from CServer::OnTimer() on the realtime audio timer thread on every frame that has connected clients, so the entire per-client audio data was deep-copied each frame. Pass it by const reference, matching the adjacent vecNumAudioChannels parameter. No behavioural change. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
Seems sensible. We'd need to check if there's really no reason for a copy though. |
|
Not sure how to parse the scope of const here, but it might be a clue that perhaps there's no reason to copy the bits. |
|
Just been looking at this, and studying the life-cycle of
Comments? |
|
OK, I've now just read the analysis on #3804, which I'd overlooked before. It largely aligns with what I came up with above (without using AI!), but reassures me that the writing and reading are protected against concurrent access. If that's the case, I would propose removing the vec parameters from |
What
CServer::CreateLevelsForAllConChannels()declares itsvecvecsDataparameter asThis PR changes it to
const CVector<CVector<int16_t>>&(by const reference), matching the neighbouringvecNumAudioChannelsparameter.Why
The function is called from
CServer::OnTimer()— the realtime audio timer thread — on every frame in which at least one client is connected:Because
CVectorderives fromstd::vector, passing by value deep-copies the entire pre-allocated member vector every frame.vecvecsDatais initialized in theCServerconstructor toiMaxNumChannels(the-usetting) worst-case stereo buffers and is never shrunk, so the copy always covers the full configured channel limit — even a nearly empty server pays it in full. This both allocates on the audio thread (which the codebase otherwise takes care to avoid — see the "no memory must be allocated" comments in theCServerconstructor) and burns memory bandwidth.A small allocation-counting reproduction of the exact copy (150 channels =
-u 150, 2×128 int16 per buffer, 375 frames/s) measured ~56,600 heap allocations/s and ~30 MB/s copied, purely from the by-value parameter; at the default-u 10it is still ~4,100 allocations/s and ~2 MB/s. The copy is not elided at-O2.Change
One parameter, declaration + definition — no behavioural change. The function only reads
vecvecsData.Testing
CONFIG+=headless, Qt 5.15) is clean.OnHandledSignal: 15).clang-format-14applied to the touched files.See #3804 for the analysis.