組込み機器向けミドルウェアを高速化する際の心得(たぶん初級)

これは [ACCESS Advent Calender](http://qiita.com/advent-calendar/2015/access) の8日目の記事です。

僕は株式会社 ACCESS 開発本部 IoT 開発1課の三原という者です。
今日は中堅プログラマの経験談を書かせていただきます。

エッセイは Qiita では禁じられているのですが、ものの見方(カッコよく言えば視点)は教えないと気づかないこともあるので、それを伝えるのだとお許し願います。

お題は、組込み機器向けミドルウェアを高速化する際の心得です。

# 高速化の限界は、アルゴリズムの限界より、デバッグ技術の限界

高速化と聞くと皆さんは「さあ高速なアルゴリズムを」とお考えでしょう。

ですが僕が見た限りでは、アルゴリズムの限界より先にデバッグ技術の限界が来ます。

ここで一つ質問。高速化パッチにまだバグが残っていると言われて、製品に取り込めるでしょうか?

よほど特別な事情がない限り、ひとまず動作している製品にバグを埋め込んではいけません。高速化するというなら、受け取った人が品質を気にすることなく製品に取り込めるパッチを作らなければならないんです。

これが無視されるとどうなるか。勤め先の恥ずかしい話を記します。

だいぶ昔のことです。口八丁手八丁のエンジニアと僕が同じチームに入りました。口八丁手八丁のエンジニアは、別チームから提供されたプログラムが遅いと言いだし、チームリーダーに改善を提案しました。そこまではよかったのですが……　彼のパッチが元プログラムのチームに渡されて試験されると実用上致命的な障害が発生しました。それをパッチを作ったエンジニアは、手元で障害が再現しないから元プログラムのチームの取り込み技術力が低いせいだと相手エンジニアの名誉を棄損し、その口八丁の説明をチームリーダーが信じて、バグ改修の責任が元プログラムのチームに押し付けられたのです。結局バグは直らず、パッチは製品に取り込まれませんでした。

何を間違えたのか。組み込めばそのまま動くパッチを提供しなかったこと、少なくともバグが出たときに書いた本人が改修に努力しなかったことが間違っていました。自分でバグを取れない、あるいは力及ばないときに助けてくださいと頭を下げられないなら、高速化の名誉がパッチ作者に与えられてはいけないんです。

# 高速化の限界は、アルゴリズムの限界より、モデリングの限界

デバッグできるようになれば「さあ高速なアルゴリズムを」と考えるでしょう。

でも、まだ課題が残っています。そもそも「問題」はなんでしょう?

組込み機器向けミドルウェアなら仕様書に沿って開発されます。仕様書はネットワーク通信や UI など外部仕様に基づいています。内部表現は制限されていないんです。

計算速度が極めて重視される数値演算の世界ではアルゴリズムが決まっていることが多々あります。スパコンの性能を比べる TOP500 では Linpack を計算することが決まっています。

それに比べて組込み機器向けミドルウェアでは解く手順は決まっていません。ぶっちゃけていえばルール無用です。F1 などのカーレースではなく、東京から大阪に向かうのに飛行機に乗るか新幹線に乗るかはたまた(道路交通法に違反しないと新幹線に勝てないのはさておき)東名高速を走るか選べるのに近いです。

果たして仕様を満たすにはどんな内部構造と手順で組めばいいか、問い直す機会が残っています。

そこまで検討すればコーディングに入れると思います。

# PC 上で早く速くしよう

組込み機器向け開発では、PC でコーディングしてエミュレータを走らせて大体のところを確認して、実機で仕上げます。最終的に評価されるのは実機上での速度です。

組込み機器の仕事についてから高速化について教えていただいた先輩のほぼ全員が「PC で速くても実機で遅いと意味がない」とおっしゃりました。

でも僕は、PC で速くしないと製品として出荷するのは無理だ、と考えています。なぜでしょう。

一つはデバッグ環境です。先に書きましたように、デバッグできなければパッチを製品に採用することができません。組込み機器ではリアルタイム OS 環境や OS レス環境など、デバッガはなく printf デバッグだけが頼りという事例も多いです。残ったバグを実機上で解決しようとすれば極めて長い時間がかかりコストに響きます。まず PC でバグを取ってから実機に載せなければいけません。

次に、これが大きなことですが、速度性能計測環境の問題があります。

高速化の鉄則は想像に任せず計測することです。

組込み機器にはプロファイラが載っていることがほとんどないため、正しい計測ができないのです。

まずは PC で速度性能を計測してボトルネックを発見しないと効率よい開発ができません。実機で動かしてあれこれ想像するより、PC 上で実装と計測のトライアンドエラーを繰り返したほうが、早く速いコードにたどり着けるんです。

すると多くの方が「実機で遅かったらどうするんだ」とおっしゃいます。

僕の経験では、C 言語で書かれた CPU 計算量が速度を決めるプログラムの場合、PC で速度差のある2つのプログラムが実機で速度が逆転することはまずありません。命令セットが違うじゃないか(x86 と ARM や MIPS)とおっしゃるかもしれませんが、現在の CPU と最適化コンパイラを用いたら、命令セットごとの得手不得手というのはほとんどないんです。

そう思って PC での性能だけ計っていると「実機のリソース量を考えないで富豪的プログラミングしても意味ないでしょ」とおっしゃる方もいらっしゃいます。

実機で動かなれば意味ないんですが、さて、実機で動かないほどの富豪的プログラミングにより高速化できる技法ってどれだけあるんでしょう?

これも経験ですが、組込み機器で扱う処理の場合、PC で特段の注意なくプログラムを書いてしまえば、いじわるのようにリソースを浪費するコーディングをしても速度はほとんど向上しません。

物理シミュレーションなどの分野ではリソース量で速度を上げられるかもしれません。

しかし組込み機器の適用分野の場合、PC のリソース量の範囲で収量逓減(最初は少量の資源で大きな収穫を得られても、だんだんと資源に対する収穫が減っていく)の限界に達してしまい消費リソース量を増やしても速度が向上しなくなります。

だから、まずは PC 上で素直に書いてみて、実機でリソースが不足したら省資源プログラムに切り替えるぐらいでいいんです。

これらの事情から、PC で開発して速度も計測して、実機では動作の確認だけする、というのが、現実の案件で実行できる範囲では最善手だったりするんです。

最後に、PC 上のシミュレータ・エミュレータと実機で同じプログラムが動かない場合があります。手を尽くしてそうなら仕方ありません。

しかし、開発現場では、API に対する誤解をしているため共通で動くプログラムを書けていないことも結構頻繁に見られます。

ここで、また、勤め先の少しだけ恥ずかしい話をします。

前のチームが OpenGL ES 2.0 を使うプログラムを残しましたが、性能目標を達成していませんでした(組込み機器としては大規模ですね)。OpenGL ES を叩くコードは PC Linux と実機 Linux で個別に最適化されており、OpenGL ES 2.0 は共通で動作するプログラムが作れない程度の互換性しかありませんと付記されていました。それを OpenGL 初心者の僕が見直したら、PC Linux 向けコードが nVidia ドライバのバグ依存で動作しており、弊社のコードの誤りを直したら実機 Linux でも動作したのです。僕が直せたのはそこまででしたが、後で優秀な方が PC Linux 上で高速化して実機 Linux でも性能目標を達成しました。

「動かない」という報告を受けたとき、ちょっと待てよと疑うことはたまに役立ちます。

# まずは中級を目指しましょう

「トップエンジニアが言うことと違う」と思われた方が多いと思います。

トップエンジニアは皆揃って、環境ごとの差異を見極めなければ最適なプログラムは作れないと言います。

でも困ったことがあります。「実機に合わせたプログラムでないと高速化できない」と言うエンジニアは全員トップエンジニアでしょうか?

PC 上で高速なプログラムを書けない、共通で動作するプログラムすら書けないエンジニアも「実機に合わせないと……」と言うんです。

トップエンジニアと初心者が同じことを言っていて言葉だけでは見分けがつかず、中級プログラマだけが違うことを言っているという状況です。

僕は、まずは中級エンジニアを目指すべきだと思います。そうしないとチームを底上げできませんから。

目標が中級ですから、この心得はたぶん初級なんです。

***

明日は @pankona さんです。よろしくお願いします。