Como sites desperdiçam CPU e bateria
(h.43z.one)- Ao investigar o ruído branco em um notebook Linux, foi descoberto que uma aba do Firefox mantinha o sink de áudio do PulseAudio ativo continuamente, mesmo sem som real
module-suspend-on-idleestava configurado para suspender o dispositivo de áudio após 10 segundos, mas quando um site cria um AudioContext, o sink não desce paraIDLE/SUSPENDED- O mesmo fenômeno apareceu em notificações web do Outlook, cliques no x.com, reprodução de voz no Google Translate e no app web do Discord; apenas
new AudioContext()já fazia o ruído começar - Nas medições, sem ruído, o uso de CPU do
pulseaudioera 0,0%, mas, com ruído, era 4,7%, e o consumo do sistema aumentava cerca de 1,5 W AudioContext.suspend()pode interromper o acesso ao hardware de áudio e reduzir o uso de CPU/bateria, mas o Firefox não interrompe automaticamente esse desperdício após algum tempo, como o Chrome faz
O problema do sink de áudio que não dorme
- Em um notebook novo, mesmo quando nenhuma música ou áudio estava sendo reproduzido, saía dos alto-falantes um ruído branco fraco, mas incômodo
- Para verificar o estado do PulseAudio no Linux, foi usado
pactl list sinks short- O estado do sink de áudio aparece como um entre
IDLE,SUSPENDEDeRUNNING - Quando o sink estava em
SUSPENDED, os alto-falantes ficavam silenciosos - Quando estava em
IDLEouRUNNING, podia haver ruído branco mesmo sem som real sendo reproduzido
- O estado do sink de áudio aparece como um entre
- Em
/etc/pulse/default.pae/etc/pulse/system.pa,module-suspend-on-idleestava carregado- Pela configuração, o sink de áudio deveria ser suspenso automaticamente após 10 segundos de inatividade
- Mas, em algumas situações, o sink não ia para
IDLE, portanto também não chegava aSUSPENDED
Por que áudio ocioso consome energia
module-suspend-on-idledesconecta do backend sinks e sources que não estejam ligados a streams por um certo período- O principal efeito é economia de energia
- Se o PulseAudio envia dados vazios para a placa de som mesmo em ociosidade, o ALSA usa mais ciclos de CPU
- Se não houver plano de manter sempre um stream ativo, configurar o timer com um valor baixo ajuda a economizar energia
A causa foi reduzida a abas do Firefox e sites
- Sempre que o ruído branco aparecia,
pactl list sink-inputsera usado para verificar qual programa estava usando o sink de áudio - A causa era sempre o Firefox
- Nenhuma das abas abertas deveria estar reproduzindo som de fato
- Também não havia vídeo silenciado
- Um pequeno script foi adicionado à barra de status do i3 para mostrar um indicador vermelho quando o sink de áudio ficasse
RUNNING - No início, o som de notificação de outlook.office.com, aberto como aba fixa, parecia ser a causa
- Ao receber um e-mail, o ruído branco começava
- Ele não parava até recarregar a aba ou reiniciar o Firefox
- Depois, o mesmo fenômeno foi confirmado em outros sites
- Em x.com, o ruído começava assim que qualquer lugar era clicado
- Em translate.google.com, o ruído também ocorria ao ouvir a voz da tradução
- O app web do Discord produzia o mesmo efeito apenas por estar aberto
Resultados de medição de CPU e energia
- Sem ruído branco, o uso de CPU do
pulseaudioaparecia como 0,0% - No mesmo ambiente, as medições de energia ficavam em geral na faixa de 4,2 a 5,1 W
- Com ruído branco, o uso de CPU do
pulseaudiosubia para 4,7% - Nesse momento, as medições de energia ficavam em geral na faixa de 5,8 a 6,7 W
- A diferença não é “insignificante” e pode ser resumida como um aumento de cerca de 1,5 W
WebAudio API e AudioContext
- Em HTML/JavaScript, as formas de reproduzir áudio se dividem em duas: a tag
<audio>e a WebAudio API - Em casos de reprodução dinâmica de som, como no Outlook, usa-se a WebAudio API, e para fazer operações de áudio é preciso primeiro criar um
AudioContext - O problema é que apenas executar
const audioCtx = new AudioContext();já faz surgir ruído branco nos alto-falantes - A página de teste fornecida é https://editor.43z.one/sqare/i
AudioContext.suspend()interrompe o avanço do tempo do contexto de áudio e suspende temporariamente o acesso ao hardware de áudio, reduzindo o uso de CPU/bateriaAudioContext.resume()reinicia o avanço do tempo de um contexto de áudio suspenso
Diferença entre Firefox e Chrome
- Muitos sites criam um
AudioContextmesmo sem precisar emitir som imediatamente e depois não o colocam em suspend - O Chrome interrompe automaticamente o desperdício de CPU/bateria depois de algum tempo
- O Firefox não interrompe automaticamente e mantém o estado em que o ruído branco continua sendo emitido
- Embora o site seja a causa direta, permanece a insatisfação de que o Firefox não protege o usuário desse desperdício de recursos
- Suspeita-se que, mesmo com fones Bluetooth conectados, clicar no x.com possa iniciar a transmissão de ruído branco e desperdiçar bateria dos fones
Solução temporária: AudioContext Suspender
- Para mitigar o problema, foi criada uma extensão que suspende automaticamente o
AudioContexte tenta retomá-lo quando o site tenta reproduzir som - A extensão não é perfeita
- Leva um pouco de tempo para retomar
- Como há vários caminhos para iniciar áudio, ela pode nem sempre retomar
- Ainda assim, é suficiente para o objetivo de uso
- Links relacionados
- Issues relacionadas no Bugzilla
1 comentários
Comentários do Hacker News
Como dá para imaginar, é bem mais complexo do que pausar o
AudioContextdepois de um certo período de silêncio, mas de todo modo acho que dá para corrigir. Outros navegadores já fizeram isso, e há trade-offs aceitáveisFonte: implementei muitos recursos nessa área no Firefox e também sou editor do padrão Web Audio API
A maioria entende as circunstâncias mencionadas aqui. Há muito a fazer e o tempo é finito
Já vi pessoalmente desenvolvedores do Firefox irem além do esperado para fazer sites muito usados funcionarem corretamente para os usuários. Sei até onde vocês vão quando um problema afeta os usuários, então espero que continuem firmes
Como cada engine de navegador e sistema operacional implementa áudio de forma diferente, se você reproduzir um som totalmente inaudível e depois gravá-lo de volta via API, surge uma assinatura
Com essa assinatura dá para verificar se o navegador está falsificando o user agent, se está em modo headless ou se é algum tipo de caso esquisito que não corresponde a um usuário real comprando um produto
https://github.com/fingerprintjs/fingerprintjs/blob/3201a7d6...
A aba não mostra o ícone de reprodução, e silenciar a aba não faz o ruído parar
Mesmo silenciando o Firefox no mixer de volume do Windows, o som não para
Edit: para usuários de desktop, essa extensão na verdade é bem ruim. O ruído branco ficando ligando e desligando o tempo todo incomoda muito mais
Por isso criei um programa próprio que mantém a saída aberta, sem enviar dados pela linha, para impedir que a saída entre em estado ocioso
Mas no contexto de notebooks isso não faz muito sentido; ali, economizar energia é o correto
No driver do Windows há uma opção para definir o stream como Always On. Queria que existisse algo assim no macOS
play -qn &iniciar no loginParece que um site de notícias reproduzia áudio enquanto carregava novos anúncios repetidamente, e por causa do áudio a aba não entrava em modo de economia. Como eu continuava recebendo cobranças inesperadas de dados, acabei tendo que mudar para o Chrome
Ou seja, não havia ruído branco, mas havia consumo de energia causado por áudio: https://bugzilla.mozilla.org/show_bug.cgi?id=1821102
Mesmo agora, no Mac, sem nada tocando, o
coreaudiodestá usando 20% de CPU. Isso está realmente destruindo a duração da bateria, então penso em mudar para outro navegador até ser corrigido, mas não quero abandonar o Firefox por algo que parece tão pequenoAtualização: parece relacionado, mas é outro bug
https://source.chromium.org/chromium/chromium/src/+/main:med...
Basicamente, quando detecta cerca de 30 segundos de silêncio, ele troca de um sink apoiado pelo dispositivo de áudio do sistema operacional para um sink nulo
Vale notar que esse método usa um relógio diferente do dispositivo de áudio, então houve alguns relatos de distorção em certos tons quando o contexto passa a ser usado de verdade mais tarde. A solução alternativa é o site usar a API suspend/resume mencionada no texto
audioContexté implementado internamente, mas parece ter muitos elementos tentando ser bem inteligentes e dinâmicos. Reproduzir um som de notificação parece algo parecido com desenhar SVG com D3 em vez de usarimg href *.svgFico me perguntando se existe algum tipo de hook de Service Worker que permita registrar coisas simples e repetíveis, como handlers de notificação, em uma API eficiente de nível mais baixo
No Firefox 134.0.1 no MacOS, não ouço nada