Почему условная переменная фиксирует наше энергопотребление?

Мы работали над проектом аудиопроигрывателя на 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
...

(Мы используем монитор активности макроса (номер энергии) и инструмент профилирования использования процессора для измерения потребления энергии.)

Ответ 1

Возможно, я ошибаюсь, но насколько я понимаю, когда вы используете условную переменную для реализации ожидания поступления данных в буфер. Главное, что он делает, он ставит поток, который отображает эту переменную условия, спящий, пока связанный с ним сигнал не просыпает этот поток. Вот почему вы получаете меньше бодрствования и более эффективно используете ресурсы.

Вот ссылки на работу с потоками в Linux, где я читал об этом:

Возможно, это даст вам некоторое представление о том, почему и как это происходит.

Снова я не совсем уверен, что я абсолютно прав, но мне кажется, что это правильное направление.

Извините за мой чистый английский.

Ответ 2

Если вы хотите максимально снизить потребление энергии на Mac или iOS, по крайней мере, вы можете использовать dispatch_semaphore_t, чтобы подождать точно до тех пор, пока буфер не будет заполнен, или не передадите некоторый блок на код заполнения буфера.