2025年1月29日水曜日

JavaScript で使えるタイマーのベンチマークを作った

JavaScript で使えるタイマーのベンチマーク js-timer-benchmark を作りました。 タイマー関数として setTimeout, setInterval がよく知られていて、 画像処理などをやっている人はこれに加えて requestAnimationFrame も知っていることでしょう。 ただ音声処理の実装をしていると、これらではうまくいかないことに気付いたので、 あまり知られていない他のタイマー機能を紹介します。

fontconv

それは AudioBufferSourceNode, OscillatorNode, ConstantSourceNode を使う方法です。 これらは音声データや正弦波、定数値を再生するためのノードですが、 これらには s秒後に再生を開始し、t秒後に再生を止める機能が備わっています。

上記を用いてタイマー機能を作成すると、方法によって様々な精度の違いが生まれます。 精度を検証するためのアプリ js-timer-benchmark はこちら。 Audio 関連のコードは Node/Deno では動かないのでブラウザ上で確認するしかありません。 まずはフォアグラウンドで初回起動時の結果がこちら。

method1sec error2sec error
setTimeout0.9ms0.6ms
setInterval (10)0.8ms0.4ms
setInterval (100)0.6ms0.4ms
AudioBufferSourceNode106.1ms2.2ms
OscillatorNode6.2ms14.3ms
ConstantSourceNode6.6ms20.2ms
requestAnimationFrame9.7ms8.8ms

フォアグラウンドで2回目以降時の結果がこちら。

method1sec error2sec error
setTimeout0.7ms0.4ms
setInterval (10)0.6ms0.4ms
setInterval (100)0.4ms0.5ms
AudioBufferSourceNode8.6ms5.8ms
OscillatorNode9.3ms19.1ms
ConstantSourceNode10.1ms20.4ms
requestAnimationFrame1.0ms0.1ms

バッググラウンドで初回起動時の結果がこちら。

method1sec error2sec error
setTimeout998.9ms900.6ms
setInterval (10)0.5ms999.7ms
setInterval (100)998.8ms999.5ms
AudioBufferSourceNode89.9ms5.8ms
OscillatorNode10.7ms19.9ms
ConstantSourceNode3.9ms20.2ms
requestAnimationFrame34985.8ms30565.4ms

バックグラウンドで2回目以降時の結果がこちら。

method1sec error2sec error
setTimeout998.4ms390.6ms
setInterval (10)1000.2ms0.4ms
setInterval (100)999.5ms999.1ms
AudioBufferSourceNode8.0ms1.3ms
OscillatorNode9.7ms19.6ms
ConstantSourceNode10.1ms19.5ms
requestAnimationFrame14580.2ms2245.5ms

まとめると、requestAnimationFrame はバックグラウンドでは停止しているようなもので使いものになりません。 setTimeout, setInterval はバックグラウンドでは非常に遅くなります。 AudioBufferSourceNode, OscillatorNode, ConstantSourceNode は同じ速度で実行できます。 音楽などはバックグラウンドで再生速度が変わったら困るので、それを考慮しているのだと思います。 AudioBufferSourceNode は初回起動時になぜか遅いですが、それ以降は高速です。 つまり、音声処理のタイマーや、バックグラウンドでミリ秒単位の高精度タイマーが必要なら、 AudioBufferSourceNode を使いましょうという結論になりそうです。 とはいえ setInterval より精度が低いのは意外でした。

ちなみにウィンドウが非アクティブのときにどのように動作するかは、Window: setTimeout() メソッド - Web API | MDN にまとまっています。 setTimeout/setInterval はベンチマークの結果通り、1秒単位で処理するようです。 記述を読む限り AudioContext を使った手法はおそらく処理速度が落ちたりはしないですが、特にそれが定まっている訳でもないみたいです。

0 件のコメント: