We were working on our audio player project on mac and noticed that the power usage was so high (about 7x that of google chrome doing the same workload.)
I used xcode's energy profiling tool, one of the problems was we had too much cpu-wake overhead.
According to xcode:
Each time the CPU wakes from idle, there is an incurred energy penalty. If the wakes are high, and the CPU utilization per wake is low, then you should consider batching work.
We had narrowed down the problem to a usleep function call.
In our code, the audio decoder is a producer that produces audio data and inserts them into the consumer -- the audio player. Our audio player is base on OpenAL, which has a buffer for the audio data.
Because the audio player can be slower than the producer, we always check the buffer availability before giving a new audio data to the audio player. If no buffer is available, we usleep for a while and try again. So the code looks like:
void playAudioBuffer(Data *data)
{
while(no buffer is available)
{
usleep()
}
process data.
}
Knowing that usleep is a problem, the first thing we did was simply removing usleep(). (Because OpenAL doesn't seem to provide callback or any other way, polling seems to be the only option.) We successfully reduced the power usage by half after doing this.
Then, yesterday, we tried
for(int i =0; i<attempts; ++i)
{
std::unique_lock<std::mutex> lk(m);
cv.wait_for(lk, 3, []{
available = checkBufferAvailable();
return available;
})
if (available)
{
process buf;
}
}
This is an experiment we tried by accident. It doesn't really make sense to us as logically it performs the same wait. And the use of the conditional variable isn't correct, because the variable "available" is only accessed by one thread. But it actually reduced our energy consumption by 90%, the cpu usage of the thread dropped a lot. Now we are better than chrome. But How is conditional variable implemented differently than the following code? Why does it save us power?
mutex lock;
while(condition is false)
{
mutex unlock;
usleep();
mutex lock;
}
...
mutex unlock
...
(We use mac's activity monitor (energy number) and cpu usage profiling tool to measure the energy consumption.)
std::condition_variable
here? How does 3 work there?) Oh, you aren't posting actual code, you are transcribing manually and have introduced an unknown number of errors and omissions. Please don't do that: please post code that actually reproduces the thing you are interested in. This may involve simplifying your existing code: if you understood what caused your interesting thing, you wouldn't be asking here! – Skycapusleep(4)
processor could be loaded up to 100%. – Eldastd::this_thread::sleep_for(std::chrono::microseconds{3});
to avoid the mutex lock/unlock and maybe it is not implemented by callingusleep()
. – Elicia