ウェブサイトがCPUとバッテリーを浪費する仕組み
(h.43z.one)- LinuxノートPCのホワイトノイズを追跡したところ、実際の音がなくてもFirefoxのタブがPulseAudioのオーディオシンクを起こし続けている問題が明らかになった
module-suspend-on-idleは10秒後にオーディオデバイスを停止するよう設定されていたが、ウェブサイトがAudioContextを作成すると、シンクがIDLE/SUSPENDEDに下がらない- Outlookのウェブ通知、x.comのクリック、Google Translateの音声再生、Discordウェブアプリで同じ現象が見られ、
new AudioContext()だけでもノイズが始まる - 測定上、ノイズがないときの
pulseaudioのCPU使用率は0.0%だったが、ノイズがあるときは4.7%で、システム電力はおおよそ1.5W増加した AudioContext.suspend()はオーディオハードウェアへのアクセスを止め、CPU/バッテリー使用量を減らせるが、FirefoxはChromeのように一定時間後に自動で浪費を止めない
オーディオシンクがスリープしない問題
- 新しいノートPCで音楽やオーディオが再生されていないときでも、スピーカーからかすかながら耳障りなホワイトノイズが聞こえる
- LinuxのPulseAudioの状態を確認するために
pactl list sinks shortを使用した- オーディオシンクの状態は
IDLE、SUSPENDED、RUNNINGのいずれかで表示される - シンクが
SUSPENDEDのときはスピーカーは静かだった IDLEまたはRUNNINGのときは、実際の再生音がなくてもホワイトノイズが発生することがあった
- オーディオシンクの状態は
/etc/pulse/default.paと/etc/pulse/system.paにはmodule-suspend-on-idleがロードされていた- 設定上、10秒間非アクティブならオーディオシンクは自動停止するはずだった
- しかし一部の状況ではシンクが
IDLEにならず、SUSPENDEDにも到達しなかった
アイドル状態のオーディオが電力を使う理由
module-suspend-on-idleは、一定時間ストリームに接続されていないシンクとソースをバックエンドから切り離す- 主要な効果は省電力である
- PulseAudioがアイドル状態でもサウンドカードへ空のデータを送ると、ALSAがより多くのCPUサイクルを使う
- 常にアクティブなストリームを維持する予定がないなら、タイマーを短く設定するほうが省電力に有利である
原因はFirefoxのタブとウェブサイトに絞られた
- ホワイトノイズが聞こえるたびに、
pactl list sink-inputsでどのプログラムがオーディオシンクを使用しているか確認した - 原因は常にFirefoxだった
- 開いているタブの中に、実際に音を再生する必要があるタブはなかった
- ミュートされた動画もなかった
- i3のステータスバーに小さなスクリプトを追加し、オーディオシンクが
RUNNINGになると赤い表示が出るようにした - 最初は、固定タブとして開いていたoutlook.office.comの通知音が原因のように見えた
- メールを受信するとホワイトノイズが始まる
- タブを再読み込みするかFirefoxを再起動するまで止まらなかった
- その後、他のウェブサイトでも同じ現象が確認された
- x.comはどこかをクリックしただけでノイズが始まる
- translate.google.comで翻訳音声を聞くときもノイズが発生する
- Discordウェブアプリは開いておくだけで同じ効果が現れる
CPUと電力の測定結果
- ホワイトノイズがないとき、
pulseaudioのCPU使用率は**0.0%**と表示された - 同じ環境での電力測定値は、おおむね4.2〜5.1Wの範囲だった
- ホワイトノイズがあるとき、
pulseaudioのCPU使用率は**4.7%**に上がった - このときの電力測定値は、おおむね5.8〜6.7Wの範囲だった
- 差は「取るに足らないレベル」ではなく、おおよそ1.5W増加と整理できる
WebAudio APIとAudioContext
- HTML/JavaScriptでオーディオを再生する方法は、
<audio>タグとWebAudio APIの2つに分かれる - Outlookのように動的に音を再生する場合はWebAudio APIを使い、オーディオ処理を行うにはまず
AudioContextを作成する必要がある - 問題は、
const audioCtx = new AudioContext();を実行するだけでもスピーカーからホワイトノイズが発生する点である - テストページとしてhttps://editor.43z.one/sqare/iが提供されている
AudioContext.suspend()はオーディオコンテキストの時間進行を止め、オーディオハードウェアへのアクセスを一時停止してCPU/バッテリー使用量を減らすAudioContext.resume()は停止したオーディオコンテキストの時間進行を再開する
FirefoxとChromeの違い
- 多くのウェブサイトはすぐに音を出す必要がなくても
AudioContextを作成し、その後これをsuspendしない - Chromeは一定時間が過ぎるとCPU/バッテリーの浪費を自動で止める
- Firefoxは自動で止めず、ホワイトノイズを出し続ける状態を維持する
- ウェブサイトが直接の原因ではあるが、Firefoxがこうしたリソース浪費からユーザーを保護できていないという不満が残る
- Bluetoothヘッドホンが接続されている場合でも、x.comをクリックした後にホワイトノイズの送信が始まり、ヘッドホンのバッテリーを浪費する可能性があるとの疑いがある
一時的な対策: AudioContext Suspender
- 問題を緩和するため、
AudioContextを自動で停止し、ウェブサイトが音を再生しようとしたときに再開する拡張機能が作られた - 拡張機能は完璧ではない
- 再開するまでに少し時間がかかる
- オーディオを開始する経路が複数あるため、常に再開されるとは限らない
- それでも利用目的には十分である
- 関連リンク
- 関連するBugzillaの課題
1件のコメント
Hacker Newsのコメント
想像できるように、一定時間無音なら
AudioContextを一時停止する、というだけの話よりはるかに複雑だが、それでも修正は可能だと思っている。他のブラウザはすでにやっているし、受け入れられるトレードオフもあるため出典: Firefoxでこの周辺機能を多く実装しており、Web Audio API標準のエディターでもある
ほとんどの人は、ここで述べられた事情を理解している。やるべきことは多く、時間は有限だ
Firefox開発者たちが、利用者の多いサイトがユーザーにきちんと動作するよう、期待以上に努力しているのを直接経験したことがある。問題がユーザーに影響するとき、どこまで対応してくれるか知っているので、引き続き頑張ってほしい
ブラウザエンジンやOSごとにオーディオ処理の実装が異なるため、完全には聞こえない音を再生してからAPIで録音し直すと、シグネチャが生まれる
そのシグネチャによって、ブラウザがユーザーエージェントを偽装しているか、ヘッドレスモードか、本物のユーザーが商品を買っている状況ではないさまざまな特殊ケースかを確認できる
https://github.com/fingerprintjs/fingerprintjs/blob/3201a7d6...
タブには再生中アイコンが出ず、タブをミュートしてもノイズが止まらない
Windowsの音量ミキサーでFirefoxをミュートしても音が止まらない
修正: デスクトップユーザーにとって、この拡張機能は実際かなり微妙だ。ホワイトノイズが何度もオン・オフするほうがずっと気になる
そこで、データをラインには流さないが出力を開いたままにするプログラムを自作し、出力がアイドル状態に入らないようにした
ただしノートPCの文脈ではあまり意味がないので、そこでは省エネが妥当だ
WindowsドライバにはストリームをAlways Onに設定するオプションがある。macOSにもあってほしい
play -qn &が起動するようにしただけニュースサイトが音声を再生しながら新しい広告を繰り返し読み込み、音声のせいでタブが省電力状態に入らなかったのだと思う。予想外のデータ料金が続いたため、結局Chromeに乗り換えざるを得なかった
つまりホワイトノイズはないが、オーディオによる電力消費があった: https://bugzilla.mozilla.org/show_bug.cgi?id=1821102
今もMacで何も再生していないのに
coreaudiodがCPUを20%使っている。バッテリー持ちを本当に台無しにしているので、直るまで別のブラウザに乗り換えようかと思うが、こんな些細に見えることでFirefoxを諦めたくはない更新: 関連はあるが別のバグに見える
https://source.chromium.org/chromium/chromium/src/+/main:med...
基本的に30秒ほど無音を検知すると、OSのオーディオデバイスが支えるシンクからヌルシンクに切り替える
ちなみにこの方式はオーディオデバイスとは別のクロックを使うため、コンテキストが後で実際に使われる際、特定のトーンで歪みが生じるという報告が何度かあった。回避策は、サイトが記事で言及されているsuspend/resume APIを使うこと
audioContextが内部的にどう実装されているかは詳しく知らないが、かなり賢く動的に処理しようとする要素が多く入っているようだ。通知音を再生することが、img href *.svgの代わりにD3でSVGを描く感じに似て見える通知ハンドラのように単純で反復可能なものを、より低レイヤーの効率的なAPIに登録できるService Workerフックのようなものがあるのか気になる
macOSのFirefox 134.0.1では何も聞こえない