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

AudioUnit allocates on the heap for every RenderCallback #8836

Open
trampster opened this issue Jun 11, 2020 · 5 comments
Open

AudioUnit allocates on the heap for every RenderCallback #8836

trampster opened this issue Jun 11, 2020 · 5 comments
Labels
enhancement The issue or pull request is an enhancement performance If an issue or pull request is related to performance
Milestone

Comments

@trampster
Copy link

trampster commented Jun 11, 2020

Steps to Reproduce

  1. Go to https://github.com/xamarin/xamarin-macios/blob/master/src/AudioUnit/AudioUnit.cs
  2. Go to line 730 in method RenderCallbackImpl
  3. Notice that for every AudioRender callback an AudioBuffers instance is allocated on the heap

Expected Behavior

No heap allocations during audio playback

Actual Behavior

A new AudioBuffer is allocated for every call for me this is 50 times a second. This will be slow, and increase the frequency of Garbage collection calls, resulting in GC pauses during playback which could impact audio quality.

Environment

You can see this by looking at the github code

Possible solutions:

  • Provide a way to specify a AudioBuffers instance for it to reuse. (if the user knows this is safe, ie doesn't hold onto it)
  • Change AudioBuffers to a struct, so it is allocated on the stack instead.
@trampster
Copy link
Author

trampster commented Jun 11, 2020

I've done some more looking at this. Each call from the native code uses the same underlying buffer (confirmed by printing the memory address of the IntPtr on each call.)

Whoever wrote the c# wrapper code doesn't seem to be worried about the caller keeping a reference to the AudioBuffers instance because it is disposed immediately. (which does nothing because in this case AudioBuffers doesn't own the pointer so the dispose returns without doing anything)

Given these two things I propose the following:
A single AudioBuffers instance is created and is reused for each call. As a safeguard if the IntPtr changes address, (it doesn't seem to) then we can create a new one for it then.

I am happy to provide a pull request that does this.

@trampster
Copy link
Author

trampster commented Jun 14, 2020

It would be nice to get an indication if such a change is likely to be accepted before I go ahead and implement it and submit a merge request.

@chamons
Copy link
Contributor

chamons commented Jun 15, 2020

Being able to improve this API to be less allocation friendly would be a solid improvement, if we can do it in a backward compatible way.

Unfortuntely AudioBuffers is a public class, so converting it to a struct would be a hard API break.

On:

A single AudioBuffers instance is created and is reused for each call. As a safeguard if the IntPtr changes address, (it doesn't seem to) then we can create a new one for it then.

I'll let @dalexsoto comment on how viable that is before you work up a PR.

@spouliot
Copy link
Contributor

@trampster the probability of acceptance depends a lot on not having breaking changes in the public API. Since AudioBuffers is public and exposed thru the RenderDelegate then this limit the number of possibilities.

	public delegate AudioUnitStatus RenderDelegate (AudioUnitRenderActionFlags actionFlags, AudioTimeStamp timeStamp, uint busNumber, uint numberFrames, AudioBuffers data);

However that does not makes changes impossible, just a bit harder (to keep existing code working).

A single AudioBuffers instance is created and is reused for each call. As a safeguard if the IntPtr changes address, (it doesn't seem to) then we can create a new one for it then.

I'm no expert on AudioUnit (or our bindings for it), other people will chime in when back at work. Still that sounds like a reasonable approach.

I suggest you to put out a more complete proposal first (it can be a pull request or here in the bug). It might take a few iterations to solve the issue, without creating compatibility problems, but it's clearly worth it :)

@chamons chamons added the enhancement The issue or pull request is an enhancement label Jun 15, 2020
@chamons chamons added this to the Future milestone Jun 15, 2020
@rolfbjarne
Copy link
Member

Also note that it looks like RenderCallback (and InputCallback) are bound incorrectly, some of their arguments should be ref arguments: #15808 (comment)

@rolfbjarne rolfbjarne added the performance If an issue or pull request is related to performance label Sep 28, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement The issue or pull request is an enhancement performance If an issue or pull request is related to performance
Projects
None yet
Development

No branches or pull requests

4 participants