AudioThumbnailCache thread and really high cpu load


#1

If I add some AudioThumbnails to my plug, the cpu goes much higher!
I did some some profiling, and I see that about half of the running time take place in the TimeSliceThread of the AudioThumbnailCache!
Is that what you would expect?

a screenshot of the profiling : http://s30.postimg.org/ovj4jux41/profiling.png

I don't do anything particular with the audiothumbnails. just create it and add it to the editor. If I remove it everything is fine.

any thought? something I should look at?


#2

Well, generating thumbnails is quite a cpu-intensive thing to do. Maybe you're causing them to be continuously re-generaed somehow?


#3

no, as far as I can see the timeSliceThread does nothing except waiting.

it just calls  wait (timeToWait) and you can see on the profiling those calls to wait.

Is waiting that cpu-intensive?


#4

I reproduced it with minimal code addition in the JuceDemoPlugin :

in PluginEditor.h, I added this minimal class :

class TestThumbnail  : public Component,
private ChangeListener
{
public:
    TestThumbnail()
    : thumbnailCache (10)
    ,thumbnail (512, formatManager, thumbnailCache)
    {
    }
    
    void changeListenerCallback (ChangeBroadcaster*) override {}
private:
    AudioFormatManager formatManager;
    AudioThumbnailCache thumbnailCache;
    AudioThumbnail thumbnail;
};


and as a member in JuceDemoPluginAudioProcessorEditor :     TestThumbnail testThumbnail;

and in the JuceDemoPluginAudioProcessorEditor constructor :   addAndMakeVisible (testThumbnail);

and the result is the same :  http://s29.postimg.org/9v5yv5hvr/profiling2.png
 

any thought?

 


#5

But your profile data shows it just waiting, not processing (?)


#6

hum.. well I don't think so, but I'm not really confident with profiling, so that's why I wonder if that is an expected result.

It's just that as soon as I add those AudioThumbnails, the cpu goes high. So to figure out what was going on, I used the Time Profiler.

I would have thought that waiting would not be a cpu demanding task, but if it's at the top of the Time Profiler, it means that the cpu is really passing a lot of time calling  __psynch_cvwait no?

 


#7

If the CPU isn't busy then of course the wait function will be taking most of the time. That's how you want it to be.


#8

yes, but I was just not expecting it to take so much!

by just instantiating an AudioThumbnail in JuceDemoPlugin, the cpu goes from 3% to 5%


#9

Really, A plugin doing nothing (no editor, no audio process) except starting 20 TimeSliceThread in the constructor is eating up 30% of my cpu. Is that really normal? PluginProcessor.h :

#ifndef __PLUGINPROCESSOR_H_526ED7A9__
#define __PLUGINPROCESSOR_H_526ED7A9__

#include "../JuceLibraryCode/JuceHeader.h"

class JuceDemoPluginAudioProcessor  : public AudioProcessor
{
public:
    //==============================================================================
    JuceDemoPluginAudioProcessor()
    {
        for (int i = 0; i < 20; ++i)
        {
            TimeSliceThread* thread = new TimeSliceThread (String(i));
            thread->startThread();
            threads.add (thread);
        }
    }
    //==============================================================================
    void prepareToPlay (double sampleRate, int samplesPerBlock) override{}
    void releaseResources() override {}
    void processBlock (AudioSampleBuffer& buffer, MidiBuffer& midiMessages) override {}
    bool hasEditor() const override                     { return false;             }
    AudioProcessorEditor* createEditor() override       { return nullptr;           }
    const String getName() const override               { return JucePlugin_Name;   }
    const String getInputChannelName (int channelIndex) const override  {   return String (channelIndex + 1);    }
    const String getOutputChannelName (int channelIndex) const override {   return String (channelIndex + 1);    }
    bool isInputChannelStereoPair (int index) const override    {   return true;    }
    bool isOutputChannelStereoPair (int index) const override   {   return true;    }
    bool acceptsMidi() const override                           {   return false;   }
    bool producesMidi() const override                          {   return false;   }
    bool silenceInProducesSilenceOut() const override           {   return false;   }
    double getTailLengthSeconds() const override                {   return 0.0;     }
    int getNumPrograms() override                               {   return 1;       }
    int getCurrentProgram() override                            {   return 0;       }
    void setCurrentProgram (int) override                       {}
    const String getProgramName (int) override                  {   return "a";     }
    void changeProgramName (int, const String&) override        {}
    void getStateInformation (MemoryBlock& destData) override               {}
    void setStateInformation (const void* data, int sizeInBytes) override   {}

private:
    //==============================================================================
    OwnedArray<TimeSliceThread> threads;
    
    JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (JuceDemoPluginAudioProcessor)
};

#endif  // __PLUGINPROCESSOR_H_526ED7A9__

PluginProcessor.cpp :

#include "PluginProcessor.h"

AudioProcessor* JUCE_CALLTYPE createPluginFilter()
{
    return new JuceDemoPluginAudioProcessor();
}

#10

Firstly, the point of a TimeSliceThread is that you only create one of them, and use it to do non-critical tasks in the background. If you need a pool of threads, that's what a ThreadPool is for. But even with a ThreadPool you'd never create 20 threads, there's no point in having more threads than your CPU has cores.

Secondly, 30% according to what? If some code is really running then your profiler will be able to show you where that 30% is actually being burned. If all it shows is idle time then you can ignore it.


#11

> the point of a TimeSliceThread is that you only create one of them,

of course, but in my real use case I'm not creating TimeSliceThread directly. I'm just creating a bunch of AudioThumnails, whose AudioThumbnailCache are creating a TimeSliceThread. So that leads to several TimeSliceThread being creating.

Or is there a way to make those AudioThumnails sharing the same TimeSliceThread? There are other classes creating TimeSliceThreads as well, so there's no way to really reduce the total number of TimeSliceThreads I'm using. 

That 30% is what the xcode monitor (or the activity monitor) says (compare to about 2% without those TimeSliceThreads). And if I got it correct, my profiler says this is burned in __psynch_cvwait

Are you saying that the time the cpu passes doing __psynch_cvwait should not be taken into account?


#12

It's your code that creates the AudioThumbnailCache object that you give to each thumbnail, and yes, of course you should only create one of them! The comments for these classes do explain that.

And like I've said a couple of times already, the time that your profiler shows in a sleep/wait call is not "burned" by the CPU, it's good for most of your time to be spent waiting. Most likely if you're creating dozens of threads, it's just the general overhead of the OS switching between all those useless threads that's showing up in the task manager.


#13

ah, good, I totally missed that AudioThumbnailCache thing.

I'm still not sure about the wait() not being shown by the monitor though. In my example, creating just 1 TimeSliceThread will double the cpu shown by the task manager (going from about 3% without any, to about 6%), which is quite huge :(