最適化の第二法則(要約版)

これはThe Second Law of Optimization (Abridged) – the Voidの日本語訳です。原文と同じく、Creative Commons 表示 - 非営利 - 継承 3.0ライセンスの下で自由に利用できます。


僕は同じ題名の元の記事(英文)がみんなが読みたいと思うよりも長いと気付いたので、要約したものを用意した。

今や誰もがこの「最適化のマントラ」を聞いたことがあるだろう。「やめとけ」

これは一般的に三つのルールに内包されている。最初の二つはこのマントラの複製で、三つ目のルールは「まだ」という抜け目のない言葉を唯一無二のルールに付け加えて「エキスパート」に宛てている。

時期尚早な最適化――僕らの大部分は身に覚えがある。この言葉がとても有名な理由だ。言うなれば格言だ。アルゴリズムを肉付けせず、結果をよく確かめもしない内に、ただ出力に驚嘆してスマートなトリックを仕込んだ経験は誰にでもあるだろう。「わかったぞ!」そう宣言する……そして半日後、もうあの馬鹿な関数を書いてたまるかと思う。まっぴらごめんだよ、なあ。

このルールは正論だ。多分ね。もう一つのルールは、その時が来たらプロファイラを使い、決して、何があっても思い込みで見積もらないことだ。そして、思い込みはきっと高くつく。これもまた正論だ。これらは格言だ、多分ね。でもこれを額面通りに受け取ると災いの元だ。

よっぽど小さいプロジェクトでもない限り、変更の前にはプロファイラを使い、他の人、特にモジュールオーナーとベテラン開発者とアーキテクトに意見を求めなければならない。変更は計画され、設計され、よく管理されたものでなければならない。プロジェクトが巨大なほど、この過程はますます重要だ。ズルはしないでくれ、頼むから。

効率的なコード != 時期尚早な最適化

先人の知恵は時期尚早な最適化を避け、確実に必要な時はまずプロファイラを使うように告げている。だけどこのように受け取ることもできる。「非効率的で膨れ上がったコードを書いていいよ、必要な時はプロファイラが見つけてくれるさ」

パフォーマンスを後付けするのはとても高くつく。本当に。だけどその代替は時期尚早な最適化じゃない。プロフェッショナルに要求される熟慮と設計を経たコードと、学生のおもちゃプロジェクトスタイルのコードの間にはわずかな境界線がある。後者は目の前の問題を解くことに注力していて、エラー処理やパフォーマンスやメンテナンスへの配慮に欠けている。

参照が頻繁に起こる場合にリストや配列の代わりに辞書やマップ*1を使うのは時期尚早な最適化じゃない。使ってもあまり複雑にならない場合に、O(n2)のアルゴリズムの代わりにO(n)のアルゴリズム(O(log2 n)のアルゴリズムがない場合)を使うのは時期尚早な最適化じゃない。同様に、変化しないデータをループから追い出すのは時期尚早な最適化じゃない。

僕はチームに自惚れた自慢屋がいるのと同じくらい、プロファイラを走らせたり同僚に話したりすることなく、乱暴な推測とでたらめな変更でコードを「最適化」しようとする奴が大嫌いだ。チームの時間が次から次へとクリーンアップに使われるのはもっと大嫌いだ。事前に二度以上考えることなくコードを書くのは簡単だ。コードを打ち込んで、走らせて、(正しくデバッグする代わりに)でたらめなトレースログを突っ込んで、コードを増やして、それを正しい出力が見られるまで繰り返すのは簡単だ。馬鹿でひどく退屈なこった。*2

僕が説明した極端にひどいケースが標準だと言ってるわけじゃない(それがどんなにありふれているかを知って驚くかもしれないけど)。僕の論点は「時期尚早な最適化」と「糞コーディング」の中庸(golden mean)が存在するってことだ。

変更のコスト

プロジェクトが開発サイクルの後期になるほど、変更のコストが指数関数的に増加することはよく書かれている(例として『Code Complete(英文)*3』を見てほしい)。「最適化のルール」のおかげで、このコストは見落とされている。パフォーマンスについて考えるべき時に考えること、少なくとも設計時に正しい判断を下すことをそのルールは著しく妨害する。

最適化指向開発を勧めてるわけじゃない。それどころか、パフォーマンスの影響に対する自覚を持つことで将来のつらい変更を避けられるんだ。繰り返したように、効率的なコードを設計して書くことは時期尚早な最適化を意味しない。僕らは賢明であり、早めに投資して将来の高いコストを避けることで精算しているんだ。現実の例として、Robert O'Callahanの投稿(英文)を見てほしい。

結論

時期尚早な最適化は有名な罠だ。コミュニティの知恵は製品コードで実験するのを避け、できるだけ最適化を延期するように告げている。コードが十分できあがって必要な時にだけ、プロファイラの助けを借りてホットスポットを判断し、それから細心の注意を払って最適化するんだ。

この戦略は非効率的で考えなしで、――時には――あからさまに醜いコードを作るように開発者をそそのかす。すべては「時期尚早な最適化の回避」の名の下に。さらに、誤ってプロファイリングがパフォーマンスを改善する魔法の解決法だとみなす。

代替が小さなコストないしコストなしに使えるのに非効率的なコードを書く口実はない。打ち込む前にアルゴリズムを考えない口実はない。あとで必要になるかもしれないとか、最適化する時にクリーンアップするつもりだからという理由で古い実験用の部品を放置する口実はない。お粗末な設計、遅いコードのコストは非常に高い。

最適化は後回しにしよう。でも効率的な、最適ではなく単に効率的なコードを最初から書こう。


*1:一般的にハッシュ関数を内部で使うことから、「ハッシュ」と呼ばれることもあるデータ構造。

*2:原文は"As dull and dead-boring as that is."だが、うまく訳せなかった。対案募集中。

*3:日本語版の書籍についてはamazon:Code Completeから見つかる。