Mac M1 thread priority & Audio Workgroups

I just got a new Macbook Pro M1 and I noticed that on a JUCE app that we’re developing, CPU performance on the audio thread is way off when using the AudioIODeviceCombiner (i.e. when using built-in speaker/mic). Not only way higher compared with a similar Intel Mac, but very inconsistent, jumping up and down. There also seems to be some correlation between the GUI/message thread and the audio thread, as when the window is resized continuously, the audio thread CPU drops and becomes stable (probably pushing the core or workgroup into P mode).

After looking over the code base and profiling, searching, etc. I stumbled on this guide from Apple on using the newish (MacOS 11) Audio Workgroups API.

I don’t see any mentions of this API in the JUCE CoreAudio code or the forums. Am I missing something obvious?

I tried hacking this into AudioIODeviceCombiner::run() myself and it makes a huge difference - the audio CPU meter becomes stable again and satisfyingly low.

Anyone else noticed this? It would be great if JUCE could look at this as I don’t want to have to use my hacky patch.

Rail

Perhaps the title of my post was misleading because i’m not so much talking about thread priority as the behavior of the audio thread, which is always set to priority of 9.

I read that thread before writing this post and it doesn’t mention anything about Audio Workgroups or issues with audio thread performance.

This post…
Joining threads to CoreAudio HAL A… | Apple Developer Forums

and this guide…
adding_parallel_real-time_threads_to_audio_workgroups

… show the relevent code for adding the device combiner thread to the audio device workgroup.

1 Like

We’re currently updating Thread to make use of modern performance classifications. These changes may well fix this issue you’re seeing, but I will check it out.

3 Likes

Thank you!

It’s simple to recreate by just taking the AudioSettingsDemo and adding some heavy processing load, like in the AudioPerformanceTest with a relatively small buffer size, and then check for xruns and fluctuating CPU usage when using AudioIODeviceCombiner VS using a single device for IO.

But, the problem seems most apparent on the faster M1 macbook pros with 10 cores. It doesn’t seem as much (if at all) of a problem on the M1 Airs for some reason.

The M1 performance issue is a known one, the higher core M1 variants have fewer efficiency cores. Which version of JUCE are you currently targetting?

When I wrote this originally I was building against JUCE 7.0.0 with XCode 13.4.1. But, I just tested JUCE 7.0.1 and there’s no improvement on the issue.

When you say M1 performance is a known issue, do you mean in general? Or as it relates to JUCE code?

Just to be clear, the issue goes away if you use the Audio Workgroup API as described above, so it seems JUCE specific to me. Also, it is not a general audio performance issue as it runs fine when you use the same device for input and output.

The pthread API used on macOS doesn’t work very well with the M1 platforms; a pthread priority below four will lock the thread to the E cores, which, ironically, on the higher performance variants causes a significant performance disparity due to having 2 vs 4 E cores.

I inquired about the JUCE version as a fix was applied recently that prevented low-priority threads. I tested our new code with AudioDeviceCombiner, and it was as stable on my M1, but I have the M1 with 4 E Cores.

We’re currently transitioning our native threading code to use modern threading priority classifications and APIs (QoS on Apple platforms).

The Audio Workgroup API is something we’re still looking into and where/if it fits into our Thread API.

2 Likes

Sorry for dumb question, but does JUCE Thread use pthread API? That is my assumption, but I never looked under the hood to see what is going on (my bad).

I also just assumed JUCE Thread class does what it says, but it seems no longer the case for all platforms?

Any news on this new Thread API ?

Thanks !

1 Like

It’s going through the final staging of review and, hopefully, be available sometime next week!

8 Likes

I see there are updates to the juce::Thread class in the develop branch for the thread priority API, but still see nothing there related to joining audio work groups. I believe that is the original point of the creator of this discussion thread - to improve audio performance by making threads join audio workgroups (on Mac systems).

In this related forum thread - rmuller provided some example code for integrating the audio render context observer callback into juce_AU_Wrappper.mm:

I still don’t fully understand how to integrate that callback with my juce::Thread threads, but it should in theory allow to join those threads to the same workgroup as the main audio callback for the au plugin - and a similar process should be possible for standalone builds.
I am following up with rmuller for some tips, but preferably would not need to hack in those changes into the JUCE API code myself.

Ideally there will be an option in the juce::Thread class to request that newly created threads are automatically joined to the same audio workgroup as the main audio callback in AudioProcessor - maybe a generic cross platform option, that then has the platform specific code handled in the underlying thread/plugin wrappers code.

Are there any updates on if/when that may happen? (oli1 - you mentioned you’d revisit it after ‘unicode support’ work).

(I would expect there could be something very similar required for audio threads in Windows at some point as new Intel processors have similar e/p core splitting.)

Thanks!

2 Likes

If anyone wants to try to try to patch this into JUCE themselves, i posted my hack here: MacOS Audio Thread Workgroups - #4 by tlacael