M1 based Mac audio load worse (nearly 2X higher) after move from JUCE 6.07 to 6.14 (but not a problem on Intel Mac)

I observed it before (and was reported by a customer), but building with the same version of Xcode (12.5) I found the audio load of my M1 native plugin & standalone build is much higher (nearly 2X audio load) after moving from JUCE 6.07 to 6.14 (also seen since 6.11) - this previously forced me to drop back to 6.07 as I didn’t have the time to investigate, was hoping this was going to go away now that I’m on 6.14 vs 6.11.

I’m running BigSur 11.6.1 on a M1 Mac Air, will try to upgrade to Monterey to see if it makes any difference.
The same build running on Intel Mac mini has no performance difference between 6.07 and 6.14 based builds. (All these are release builds with full signing/certification)

I’m not sure where to start with figuring out what is going on here - looking for suggestions and comments. Also wondering if anybody else noticed this?

Another thing I noticed with JUCE 6.14 vs 6.07 was a potentially different way thread priorities are set on Mac builds (not confirmed)… at least was my observation that a timeslice thread was causing a much bigger issue after upgrade to 6.14, and one fix was to drop it’s priority (the issue was much more apparent because I was reading files in the timeslice thread - and it was significantly affecting editor opening times - but not in the 6.07 based builds) .

(The reason I’m still on Xcode 12.5 is a previously observed and JUCE developer confirmed issue with Xcode 13 compiler disabling optimizations for the function/register settings which disable denormalized numbers… so I rolled back to 12.5 have stuck with it, until something is confirmed with that issue, but that is discussed in a different thread here on the JUCE forum. - EDIT: I just realized that there was a workaround (volatile keyword) to fix that compiler issue pushed in the latest JUCE versions, so it should in fact be OK to upgrade to Xcode13 for my particular concern about that. Whether or not that makes any difference with Mac M1 performance I have no idea.)

Followup on this - I updated to Xcode 13.2.1 - currently the latest - and regenerated my release package/signed/stapled etc. and still seeing the same large performance difference on the prior JUCE 6.0.7 based build (better) vs the new 6.1.4 based build (worse) - confirming again that both these builds are native/universal, and no performance change happened for the Intel Mac version of the build.

Edit… I know some folk are going to say, RTFM :slight_smile: but I do see something about thread handling in the JUCE change notes - 6.1.0 “Thread::setPriority() will no longer set a realtime scheduling policy for all
threads with non-zero priorities on POSIX systems.” - maybe this is something to do with it.

edit - although, for my audio threads I’m using startThread(Thread::realtimeAudioPriority) - realtimeAudioPriority=-1 which is audio thread priority equivalent to priority 9… in JUCE code comments theres a mention of an exception to that for Android.

Since you mentioned that, what happens when you pass 10 as priority?

It would make it easier to find the source of the problem if you could git bisect on JUCE to find out what exact commit introduced it

1 Like

Thanks - that is a good suggestion - I have to try - assuming it can work for me (as I have a slightly customised version of JUCE with some modifications of UI widgets), it would be the first time I’ve used it.

1 Like

I tracked this down to the following commit (I was surprised it’s actually just after 6.0.7 tag):

1 parent [99112cf]
https://github.com/juce-framework/JUCE/commit/8fc1195c353d9dd47fe9e28a8d35dbc7e205dee1

commit 8fc1195c353d9dd47fe9e28a8d35dbc7e205dee1

If I comment out the following code and related ‘addMethod’ call, the processing load on my M1 mac build goes back to normal. Note that I am still on Big Sur 11.6.1 on my Mac Air M1.

    static void viewWillDraw (id self, SEL)
    {
        // Without setting contentsFormat macOS Big Sur will always set the invalid area
        // to be the entire frame.
       #if defined (MAC_OS_X_VERSION_10_12) && MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_12
        CALayer* layer = ((NSView*) self).layer;
        layer.contentsFormat = kCAContentsFormatRGBA8Uint;
       #endif

        sendSuperclassMessage<void> (self, @selector (viewWillDraw));
    }

I don’t fully understand the above code - obviously it is something related to drawing/refresh.
The test patch I have been using for comparisons includes some animated traces, including an Oscilloscope (based on DrowAudio code). My plugin/standalone doesn’t use open GL, just the built in JUCE widgets/draw functions, and where possible I don’t redraw things unless they are animated/moved.

My Intel i5 based Mac Mini is also running Big Sur (11.5.2) and doesn’t exhibit the increased load seen with that commit.
My builds currently use Mac OS 10.11 as the minimum deployment target.

If I build with JUCE 6.1.4 (currently the latest I have synced to), and if I comment out that function, the CPU/audio load is also back to expected performance on the M1 Mac air / Big Sur.

2 Likes

I didn’t think it would be a good idea to set my audio processing threads higher than the main audio process/callback thread priority, anyway, turns out that the extra load appears to be coming from something related to screen redrawing.

Hi @yairadix
Now that I found the culprit, I am not sure if it’s because of my build configuration, or if this is a general issue for M1 builds that would affect others.
What do you suggest? Is this something to raise to JUCE devs?
I certainly still don’t understand the exact cause of the issue, and don’t feel comfortable just commenting out code that was added that I don’t fully understand even if it fixes my issue.

Thanks.

1 Like

Searching for the code snippet from that commit brings up that this Apparent serious JUCE painting issue in Big Sur - #39 by djb-2 is the relevant thread describing why this change was introduced.

Note the quote there:

It seems now you have new evidence to offer on that and I suggest joining the thread.
As a workaround it appears that maybe attaching an OpenGL context (i.e using the OpenGL backend) might help performance (though the colors would be a bit off if not using the SR branch)

1 Like

To recap, reverting this commit results:

  • resolves performance on M1 Mac Mini
  • doesn’t regress / change on Intel based macOS?

one note, both devices use Integrated Graphics, would be nice to check on AMD based Intel mac.

Hello, this issue is specifically seen on my M1 Mac Air laptop, and not apparent on a Core i5 Based Mac Mini - both are running Big Sur, both running the same plugin build (compiled with JUCE 6.1.4 and only using built in GUI widgets/paints - no Open GL).
Simply removing the extra function significantly drops the audio load on the M1 build, the Intel build was not affected.
Unfortunately I don’t have access to any other M1 based systems, but I do have a Mac Pro laptop - it has Intel Iris Plus graphics - I assume that’s built into the Core i5 that it has.

I’ve been trying to reproduce this issue, but I’ve not had any success.

Can you see this issue in any of the apps bundled with JUCE like the DemoRunner? Otherwise would it be possible to create a sample app that demonstrates the problem?

1 Like

Thanks Tom.
I think it might be difficult to reproduce this with apps in the demo runner, since they are not going to be running such complex multi-layer sounds with multi-threaded voice allocations whilst doing so many things (many ui components, some with animations) in the UI at the same time.
I will see if I can try an audio demo from demo-runner, but suspect a huge difference wont show - unless there is a demo that can replicate a similar scenario as my plugin. I can only confirm if I removed that one function and re-build, the CPU load dropped significantly for my M1 based build - I narrowed it down to that one very specific commit, so it has to be related (and is specific to the M1 based Mac).
It’s possible this issue might go away after I update the OS on my M1 Mac air … I have not done it yet, as wanted to first narrow down the issue to that specific commit.

I think we can discount a lot of the complexity that you mentioned. Audio processing and GUI updates are only weakly coupled - if the GUI thread is taking more time to render then it will, in general, slow everything else down.

If we can reproduce any circumstances where the time taken to call drawRect is larger with layer.contentsFormat = kCAContentsFormatRGBA8Uint then that will give us something to dig into. I suspect this will be due to having multiple moving elements on different parts of the GUI.

I hate to re-open this discussion, but given that I just saw recent postings about how threads on M1 processors might get allocated to e-cores instead of p-cores, and since there appears to be no control over thread priority through JUCE Thread on Apple Silicon (assuming what was mentioned in FR: Thread-Priority vs Efficiency/Performance Cores is correct) - is it possible that some of my audio processing threads got assigned & dropped priority to e-cores and then were impacted by other e-core threads at same priority such as graphics refresh related threads - thus holding up completion of my audio callback tasks? This might explain why graphics repaint related code appeared to severely impact the audio processing load on my M1 builds and not on the same Intel Mac build.