ジンジャー研究室

長めのつぶやき。難しいことは書きません。

速度との戦い

オーディオプログラミングは速度との戦い。特にリアルタイム処理となると常にパフォーマンスを気にしていないとすぐに死んでしまう。 なぜなら、再生に間に合うようにデータを用意しないと音が途切れてしまうから。例えば、サンプリング周波数が 48000Hz だとすると1サンプルあたり 0.02ms で処理しないと再生速度に負ける。ステレオにして沢山エフェクトをくっつけて同時発音数を増やしてとやっているとすぐに超えてしまう。

このパフォーマンスチューニングが個人的には結構面白い。 お仕事は Web の主にフロントエンドなので、パフォーマンスを気にせず富豪的に処理しても大体なんとかなってしまう。例えば React とかは Virtual DOM を immutable に作っては捨ててというのを繰り返しているので、まあ GC が頑張っているはず(もちろんメモ化はできるけど)。 しかし話がオーディオになると真逆のプログラミングスタイルになる。とにかくループ中にメモリを新たに確保しないようにあらかじめ用意した領域を使い回す。 immutable とか何それ美味しいの。GC だけではなく、ちょっとした処理が意外と速度に影響するのが新鮮で驚く。

まず文字列結合が遅い。例えば、"lfo" + index + "_freq" などというキーワードを作るのをループの中でやっていたのでかなり影響が出ていた。パターンがあらかじめ決まっているので ["lfo0_freq", "lfo1_freq", "lfo2_freq"] という配列を作ってインデックスアクセスすることで改善した。

さらに文字列の比較も遅い。上のようなキーワードは enum で実体を int にしておくだけで改善が見られる。

周波数をノート番号に戻すのに math.Log2 を使っていたが、この計算も遅かったので予めノートから周波数を計算しておいて2分探索したら速くなった。

その他、無駄な計算をすると当たり前だがその分遅くなる。

こういう一つ一つの処理・改善がダイレクトに響く。普段は IO が支配的すぎて計算処理のコストはゼロとして考えていたり、考えたとしてもオーダーが爆発しないかを気にするくらいだったり、あとはブラウザだとレイアウト計算をいかに減らすかを気にするくらいだけど、文字列処理が遅いとかは「昔そういう話を聞いたことがあるけど本当だったんだ!」という感じで純粋に面白かった。貧民の精神で行こう。