Мы работали над проектом аудиопроигрывателя на Mac и заметили, что потребление энергии было настолько высоким (около 7 раз больше, чем у Google Chrome, выполняющего ту же рабочую нагрузку.)
Я использовал инструмент для профилирования энергии xcode, одна из проблем заключалась в том, что у нас было слишком много накладных расходов процессора.
В соответствии с xcode:
Каждый раз, когда процессор просыпается от холостого хода, возникает понесенный энергетический штраф. Если будильники высоки, а загрузка ЦП на бодр на низком уровне, вам следует рассмотреть возможность пакетной работы.
Мы сузили проблему до вызова функции управления.
В нашем коде аудиодекодер является производителем, который производит аудиоданные и вставляет их в потребитель - аудиоплеер. Наш аудиоплеер базируется на OpenAL, у которого есть буфер для аудиоданных.
Поскольку аудиопроигрыватель может быть медленнее, чем производитель, мы всегда проверяем наличие буфера, прежде чем передавать аудио данные аудиодатчику. Если буфер не доступен, мы некоторое время проспали и повторите попытку. Таким образом, код выглядит так:
void playAudioBuffer(Data *data)
{
while(no buffer is available)
{
usleep()
}
process data.
}
Зная, что спящий является проблемой, первое, что мы сделали, это просто удалить usleep(). (Поскольку OpenAL, похоже, не обеспечивает обратный вызов или каким-либо другим способом, опрос кажется единственным вариантом.) Мы успешно сократили потребление энергии наполовину после этого.
Затем вчера мы попробовали
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;
}
}
Это эксперимент, который мы попытались случайно. Это не имеет для нас никакого смысла, поскольку логически он выполняет одно и то же ожидание. И использование условной переменной неверно, потому что для переменной "доступно" доступен только один поток. Но это фактически сократило потребление энергии на 90%, использование процессора в потоке сильно упало. Теперь мы лучше, чем хром. Но как условная переменная реализована иначе, чем следующий код? Почему это спасает нас?
mutex lock;
while(condition is false)
{
mutex unlock;
usleep();
mutex lock;
}
...
mutex unlock
...
(Мы используем монитор активности макроса (номер энергии) и инструмент профилирования использования процессора для измерения потребления энергии.)