Мы работали над нашим проектом аудиоплеера на Mac и заметили, что энергопотребление было очень высоким (примерно в 7 раз больше, чем у Google Chrome, выполняющего ту же нагрузку).
Я использовал инструмент профилирования энергии XCode, одна из проблем заключалась в том, что у нас было слишком много служебных сигналов процессора.
Согласно xcode:
Каждый раз, когда процессор выходит из режима ожидания, происходит наложение энергии. Если количество пробуждений велико, а загрузка ЦП на пробуждение низка, то вам следует рассмотреть пакетную работу.
Мы сузили проблему до вызова функции usleep.
В нашем коде аудиодекодер является производителем, который производит аудиоданные и вставляет их в потребитель — аудиоплеер. Наш аудиоплеер основан на OpenAL, который имеет буфер для аудиоданных.
Поскольку аудиопроигрыватель может работать медленнее, чем производитель, мы всегда проверяем доступность буфера, прежде чем передавать аудиоданные новым аудиоданным. Если буфера нет, мы некоторое время спим и пытаемся снова. Итак, код выглядит так:
void playAudioBuffer(Data *data)
{
while(no buffer is available)
{
usleep()
}
process data.
}
Зная, что usleep — это проблема, первым делом мы просто удалили 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
...
(Мы используем монитор активности Mac (номер энергии) и инструмент профилирования использования процессора для измерения потребления энергии.)
Возможно, я ошибаюсь, но насколько я понимаю, когда вы используете условную переменную для реализации ожидания поступления данных в буфер. Главное, что он делает, он помещает поток, который отображает эту переменную условия, в спящий режим, пока связанный с ним сигнал не разбудит этот поток. Вот почему вы меньше просыпаетесь и используете ресурсы более эффективно.
Вот ссылки на работу с потоками в Linux, где я об этом читал:
Может быть, это даст вам некоторое понимание, почему и как это происходит.
Опять же, я не совсем уверен, что я совершенно прав, но мне кажется, что это правильное направление.
Извините за мой чистый английский.
Если вы хотите максимально снизить энергопотребление на Mac или iOS, по крайней мере вы можете использовать dispatch_semaphore_t для точного ожидания заполнения буфера или передачи некоторого блока в код заполнения буфера.