オーバーヘッドの考察

0から組み立ててきたFIRフィルターもようやく音が出てくるようになりました。中心となるアルゴリズムはADSP-2191の性能をきっちり100%引き出しています。このあたりで最後の仕上げと行きましょう。

プログラムの性能は核となるアルゴリズムだけでは決まりません。高級言語を使用することによるオーバーヘッドなども考えた上でシステムの性能を見積もる必要があります。プロの場合、こういった見通しや評価は必須です。アマチュアの場合も何かと好奇心をくすぐる話です。

今回は組み立てたプログラムを元にオーバーヘッドの評価を行い、これを改善できるかどうか見て見ましょう。

プログラム構造の評価

アルゴリズムだけを評価すると今回のプログラムは一番内側にFIRのループがあり、その外側にbufSize回のループがあります。外側のループは入力の端から端までサンプルを舐めていくループです。この二重のループのアルゴリズムをPAD図で表すと下のようになります。PAD図はあまり広く知られていませんが、アルゴリズムを構造的に表すのに便利な記法です。下の図で両側に縦線の入っている箱は処理の名前、左に縦線の入っている箱はループを表します。箱の右側にぶら下がっている処理はネスティングを表します。この図を見ると、AM音質というアルゴリズムは2重のループの中で積和演算を行うことがわかります。

さて、この表現はアルゴリズムだけです。では関数呼び出しやインラインのオーバーヘッドはどうでしょう。ここでその内容を論じる前に勘のいい人なら頭の中でクエスチョン・マークが点滅していることだと思います。「インラインって、オーバーヘッドがあるのか?」。あるのです。インライン展開したコードへはレジスタ経由でパラメータを渡します。しかし、内部処理で使うレジスタと値を渡すレジスタは異なるものになります。その結果、余計なレジスタ間移動が行われてしまいます。マニュアルにはコンパイラがこの手のオーバーヘッドを最適化するようなことを書いていますが、VisualDSP++3.0を見る限り、これは最適化されません。そういうわけで、インライン・アセンブリにもオーバーヘッドがあります。

こういったことを頭において、オーバーヘッドまで表せるようPAD化したのが下の図です。このPAD図では、handleBuffer関数の中でfilter関数をbufSize回呼んでいるのがわかります。また、フィルター関数の中にはインライン処理が一つあり、インライン処理の中で積和をtaps回繰り返していることがわかります。なお、この表記はオーバーヘッドの見積もりが目的ですので若干簡略化してあります。

単一サンプル処理

オーバーヘッドを見込んだ改良

上の(甲)の図を見ると、この処理は外側のループの中でサブルーチンfilterを呼んでいることがわかります。サーブルーチンの呼び出しにはオーバーヘッドがあります。そこで外側のループをルーチンの中に詰め込んでしまえばこのオーバーヘッドを排除できます。それが下の(乙)です。

ブロック処理(1)

(乙)では繰り返しがfilter関数の中に組み込まれましたので関数呼び出しオーバーヘッドを削ることができました。しかし、まだインラインのオーバーヘッドが残っています。それを最適化したのが下の(丙)です。

ブロック処理(2)

ここまで来ると、インラインの中に二重ループを詰め込んでいますのでADSP-2191の能力を出し切っているといえます。反面、パラメーターの受け渡しを含めたプログラマの負担は大きくなります。これほど大掛かりになるのであれば、むしろルーチン全部をアセンブリ言語で書くことも考えるべきかもしれません。

計測結果

では、上の三つのプログラムの処理時間を計測してみましょう。計測は甲、乙、丙のアイドル時間と畳み込みの時間に対して行います。畳み込みはtaps回ですが、実際にはループの中ではtaps-1回しか実行しませんので計測結果もtaps-1回のものであることに気をつけてください。いずれにせよtapsが61ですので誤差はわずかです。また、アイドル・ループはジャンプ命令とnop命令からなりますので両者の和をアイドル時間とします。測定はEZ-KIT Liteのプロファイラ機能を使って行います。

下の表が測定結果です。単位は%です。

プログラム アイドル時間 畳み込み時間 オーバーヘッド
90.9 3.65 5.4
91.4 3.60 5.0
94.1 3.58 2.4

オーバーヘッドは100-(アイドル時間)-(畳み込み時間)で算出します。この結果をみるとfilter関数の呼び出しオーバーヘッドは予想外に小さいことがわかります。反対にびっくりするほど大きいのがインラインのオーバーヘッドです。これは今回の関数はMxレジスタやLxレジスタを設定していること、mcmに対応するために任意ステップのバッファにアクセスできるようパラメータを用意したことからインライン・アセンブリに与えるパラメータが10個に達していることに原因があります。コンパイラの最適化がインライン・アセンブリに対しても行われれば乙のオーバーヘッドはもっと小さくなったでしょう

オーバーヘッドはmcm自身のオーバーヘッドを含みますが、丙では十分小さくなっているといえます。2.4%という値はサンプルあたり40サイクルであり、mcmのを含めたC++言語の生産性を考えると満足していいと思います。

ところで、今回のプログラムのCFIRはメンバー関数をすべて静的バインディングにしました。そこでvirtual関数化したときのオーバーヘッドを計った結果が下の表です。

プログラム アイドル時間 畳み込み時間 オーバーヘッド
甲(virtual) 89.3 3.6 7.1

この結果と甲の結果から仮想関数の呼び出しオーバーヘッドが1.7%とわかります。これを1回の呼び出しあたりに直すと28サイクルとなり、ちょっと大きすぎるなと感じます。ADSP-2191のC/C++対応機能は後から付け足したものであり、若干の効率の悪さはあるようです。mcmのように処理をブロック化してしまえば気になりませんが、サンプルごとに処理をするときには気をつけたほうがいいかもしれません。

まとめ

firフィルタの説明から始まったシリーズもようやく幕を閉じます。ADSP-2191は最近のDSPの中にあっては性能的に見るべきものはありません。しかし、160MHz位あれば、48KHzステレオであっても2000タップくらいはかますことができます。意味はないですけど。

今回の実験で十分遊びがいのあるプロセッサであることはわかりました。また、C/C++を使う上で性能にどの程度気を使えばいいかもある程度把握できました。今後の参考にできるかと思います。

プログラム

今回も全プログラムをダウンロードできるよう用意しておきました。解凍してEZ-KIT Liteから利用してください。

おわり

2191空挺団 | プログラム | EZ-KIT | こぼれ話 | アーキテクチャー | 命令 | レジスタ | DSP掲示板 | FAQ |