パフォーマンスへの道は地雷コードで敷き詰められている

パフォーマンスチューニングでは、多くの場合、コードの変更が必要になります。そしてどんなコードにも必ず、複雑すぎる部分や、依存性が高すぎる部分が含まれているものです。そういうコードは、いわば地雷のようなもので、途中で昨裂してチューニングを妨げます。地雷の昨裂により第一に犠牲になるのは、当然のことながら、プロジェクトのスケジュールです。作業の進行速度が常に一定で、途中で、特に大きな問題が起きないのであれば、いつ完了するかの予測は簡単でしょう。しかし時折地雷が昨裂するとなると、完了時期の正確な予測は極めて難しくなります。

たとえば、プログラムにホットスポットを発見した場合を考えてみましょう。その場合、通常はそのホットスポットで使用されているアルゴリズムを改善するという対処をすることになります。その作業の所要時間を上司に見積もれと言われ、「3〜4時間です」と答えたとします。しかし作業をしてみたら、実は修正箇所に依存しているコードが他にあり、影響を与えてしまうとわかるかもしれません。もちろん、元々密接に関係している部分が依存し合うのは当然なことなので、それを事前に予測し、依存関係を考慮に入れた計画を立てることも可能でしょう。壊れた依存関係を修復する作業も発生するという前提で計画を立てるのです。ただ問題なのは、その修復によってまた別の依存関係が壊れる恐れもあるということです。そして、壊れる箇所が、元々の修正箇所との関係が薄いところだとしたら、そもそも壊れると予測するのが難しい、ということもあります。その場合には、スケジュールを立てる段階で考慮に入れるのも困難です。「3、4時間」と見積もった作業に3、4週間かかるようなことも簡単に起きてしまいます。予測外の問題が1つ見つかる度にスケジュールが1、2日遅れるというのも珍しいことではありません。あっという間に終わるはずと,思って始めたリファクタリングの完了に、結局何ヶ月もかかることもよくあるのです。そんなことがあると、作業を担当したチームは信用も立場もなくしてしまうでしょう。下手をするとチームや会社の存続すら危うい事態になってしまいます。せめて、事前にそういうリスクが存在すると察知する手段、あるいはリスクの大きさを評価する手段でもあればいいのですが…..。

実は、コードの依存性、複雑さを計測するための手段や、それをコントロールするための手段というのは数多くあります。たとえば、「ソフトウェアメトリクス」です。ソフトウェアメトリクスというのは、コードの様々な特性を定量的に評価した指標のことです。コードメトリクスの値は、コードの品質に大きく関係します。その中でも、コードの依存性を計るのに使えるのが、「ファンイン(fan-in)」と「ファンアウト(fan-out)」の2つです。たとえば、クラスのファンアウトというのは、あるクラスが直接、または間接的に参照しているクラスの数のことです。別の言い方をすれば、あるクラスをコンパイルする前にコンパイルしなくてはならないクラスの数、ということになります。一方、クラスのファンインというのは、あるクラスに依存しているクラスの数のことです。ファンアウトとファンインがわかれば、I = f[o] / (f[i] + f[o])という式を使って不安定因子(instability factor)を計算することができます。I0に近いほど、パッケージは安定しているということになります。逆にI1に近づくほど、パッケージは不安定です。安定したパッケージほど、修正を加えることによるリスクは低いと言えます。反対に、不安定なパッケージは「地雷」が多いと言えます。リファクタリングでは、IOに近づけることが目標になるでしょう。

メトリクスを使う際に注意すべきことは、これはあくまでおおまかな指標である、ということです。純粋に数学的に言えば、f[o]が増えずにf[i]だけ大きくなっても、I0に近づくことになります。しかし、ファンイン値が極端に大きくなるのは問題です。ファンイン値が大きいというのは、あるクラスに依存するクラスが多いということなので、その依存関係を壊さずにクラスに修正を加えるということが非常に難しくなってしまうのです。また、ファンアウト値が減らなければ、Iがいくら0に近づいてもリスクは滅りません。つまり、両者のバランスが大事ということです。

ソフトウェアメトリクスを使うデメリットとしては、扱うべき数値が膨大になりやすいということがあげられます。メトリクスツールが一度に大量の数値を出力すると、慣れないうちは圧倒されてしまうかもしれません。それでも、ソフトウェアメトリクスが、コードをクリーンにする上で非常に役立つ手段であるというのは間違いありません。パフォーマンスチューニングの作業に深刻な悪影響を及ぼす前に、コードの地雷を発見し、排除するのに役立つのです。