Crazy CPU Usage?!

Hey lads, I’ve built a simple audio player extending on the juce tutorial project…I’ve added few functionalities like the possibility of queueing up different tracks and moving through them while playing (For now part of the UI implementation is not done yet.

My main question is…what is wrong with it? :rofl: Why such a huge cpu usage (xCode shows an average of 70% as soon as you drop a file in without even playing it)?

Here is the Repo, I’ve stuck everything on one page for now so no OOP at the moment (is not even 500 lines).

I have just ran a test on xCode threadSanitizer and apparently this is the only data race happening:

  void pushNextSampleIntoFifo(float sample) noexcept
    {
        // if the fifo contains enough data, set a flag to say
        // that the next line should now be rendered..
        if (fifoIndex == fftSize)
        {
            if (!nextFFTBlockReady)
            {
                std::fill(fftData.begin(), fftData.end(), 0.0f);
                std::copy(fifo.begin(), fifo.end(), fftData.begin());
                nextFFTBlockReady = true;  //data race
            }

            fifoIndex = 0;
        }

        fifo[(size_t)fifoIndex++] = sample;
    }

    void timerCallback() override
    {
        if (nextFFTBlockReady) // data race here 'nextfftblockready
        {
            drawNextLineOfSpectrogram();
            nextFFTBlockReady = false;
            repaint();
        }
}

For brevity’s sake I have only posted part of the code, I am quite ignorant when it comes to multi-threading and I’m just getting into it, any solutions you guys know would be very appreciated!! :smiling_face_with_three_hearts: :smiling_face_with_three_hearts:

Are you definitely building in Release mode, rather than Debug?

Yes unfortunately I am, I have sorted the first data race occurrence with the use of std::atomic_bool, but the CPU usage has not changed much, also, I’ve found another one:

Data race in std::__1::unique_ptr<juce::AudioFormatReaderSource, std::__1::default_delete<juce::AudioFormatReaderSource> >::get() const at 0x7ba800006000

Fixing race conditions is not going to help with the performance problems, they are separate issues.

For performance problems you need to use the profiler to see which parts of the code use the most the CPU.

1 Like

ok…it has to do with the paint function and the rendering of the FFT data, commeting that bit of code dropped the cpu usage from 70 to 30% (which i honestly think is still too much for an audio player)…i’ll try to find ways to optimise that part of the program!

I reduced the timer callbacks (basically the rendering FPS i guess) from 60 to 20…cpu usage dropped heavily.

I have a few suggestions:

  • The OpenGL renderer could be your friend here. It’s very fast at drawing images to the screen.

  • I recommend moving your spectrogram rendering code to its own thread. JUCE’s Thread class has wait/notify synchronisation built in that would be suitable for your render thread while it waits for the FFT buffer to fill up.

  • I recommend JUCE’s AbstractFifo for simple FIFO implementations, it’s use of atomics makes it thread safe for single consumer/producer situations.

  • spectrogramImage.moveImageSection(0, 0, 1, 0, rightHandEdge, imageHeight); Is a heavy operation, I would suggest making your image twice as wide as your visible area and use the ability to only draw a section of it while you write to another. You can then render each line of the spectrogram sequentially onto your image, wrapping around to the beginning as you reach the end, doing the same on the UI drawing side (but as view width sized blocks), creating the illusion of a scrolling spectrogram but without the costly overhead of shifting the image data.

2 Likes

the easiest way to reduce cpu load in GUI rendering is to just draw smaller things and be very reserved about the amount of stuff on the screen that needs repainting. unfortunately a spectogram updates across its whole component everytime. maybe a good time to get into openGL

1 Like

To be honest I would not know how to implement your third suggestion (yes I’m that bad :joy: :sweat_smile: ) but I’ll take the chance to have a look into openGL (as you both mentioned it)! Some great insights, thanks!!

Can you explain why you want(ed) to use 60 frames per second?

No reason at all…the rendering part of the project is taken from a Juce Tutorial too (in which the timer speed is set to 60hz), also I had no idea that if not told otherwise the paint function would simply run on the main thread.

Actually, I would like some advice on how to understand on which thread functions are working…are there any getters that would let me see/print out their thread?

You can view the threads I’m the debugger. And within a thread you can use some of the Thread class APIs to access the thread ID, if you are tracking your own threads.

1 Like