まだ重たいCMSをお使いですか?
毎秒1000リクエスト を捌く超高速CMS「adiary

2006/06/10(土)プログラムの実行速度と形式美

またもデータベースカラム追加

adiary ては、各ユーザーあたり3つのテーブルを使用しています。

  1. 日記本文のテーブル
  2. コメントのテーブル
  3. トラックバックのテーブル

従来から、日記本文のデータの中に、その日記に付いたコメント数やトラックバック数というデータを持たせていました。データベースの正規化の立場からすると、やってはいけないことなのですが、アクセスの度にいちいちSQLで集計とってたら遅くてかなわないということで。

今ではそれを逆に利用して、コメント数やトラックバック数が0のときは、そもそもコメントやトラックバックをロードするSQLを発行しないようにしています。

さらに一歩進めて。今回、コメントの一覧やトラックバックの一覧を機能をつける際*1、コメントごとに元記事のタイトルやら日付やらを引っ張ってくるようにしました*2。ですが、たかだか元記事の「日付」と「タイトル」だけを取得するために、コメント1件につき1回SQLを発行するのはなんとも効率が悪すぎる。

今回の変更で、コメントテーブル、トラックバックテーブル双方に元記事の「日付」と「タイトル」を保存するようにしました。せっかく正規化されているデータを分散させるなんて「なんてことだ!」とか言われそうですし、不整合が起こらないように管理するのが面倒ですが、背(形式美)に腹(処理速度)は代えられません

実はadiaryには、処理速度や見通しのために、プログラムの重複記述*3を敢えて行っている箇所が何カ所かあります。

*1 : 最近のコメント、最近のトラックバックでも同様に使用しています

*2 : つまり、コメントが参照している日記キー(diary_pkey)から日記本文をjoin……なのですが、擬似データベースも使えるようにする都合上、データベースではjoinせずにプログラム側でjoin相当の処理をしていました

*3 : ルーチン化可能だけど敢えて行っていない

よくみるプログラムたち

1.一本道プログラム

要求された処理を満たすようにだけ書かれた、能率も形式美も何もない「のっぺり」としたプログラム。いわゆる「プログラムを書ける人」の7割ぐらいが*4、こういうプログラムを書きます。

必要な処理が順列組み合わせで、なんの抽象化も、構造モデリングもなされずに記述され、大抵の場合はメンテ不可能なまでに*5巨大化し、下手をしたら1/3のソース量で同じ処理が書けるんじゃないか? と思えるものです。

2.形式美プログラム

機能ごとにソースが分離され、処理はきれいなまでに抽象化され、またはクラス化(オブジェクト指向)が徹底されて、非常に美しいプログラム。である反面、メモリの多重確保やメモリ間コピーなどは意図にも介さない。

事務系のプログラムやミッションクリティカルなプログラム(たとえば銀行システムや金銭を扱うような通販システム)では能率なんて求めず安全第一で書くべきだけど、処理速度や処理効率が(極端に)求められるプログラムである場合はときとして失格*6。頭の良い人がプログラムを教科書的に学んだ際に陥りやすい。

3.効率主義プログラム

自分はこのタイプ。限られたリソースや有限な資源を用いて、または計算量がNPであるような大問題を効率よく求めたときなどに書くプログラム。一見するとタイプ1の「一本道プログラム」と似て映るが、きちんと眺めれば効率を求めたものか、何も考えずに書きつづったものかは一目瞭然。

また、形式美をある程度理解した人間が書いた効率主義プログラムと、そうではない効率主義プログラムではメンテ性という意味で大きな差が出てくる。実行効率は形式美とある程度両立可能であるが、たまに調子に乗って必要以上に高効率を求め*7無駄にソースが見づらくなることもある(T-T

4.adiaryは?

  1. perlがあればどこでも動くという汎用性
  2. 抽象化され、差し替え可能な構成と形式美
  3. それでいて限定された環境でも高速に動くこと(cgi動作 & pure perl)
  4. 恵まれた環境ではさらに高速に動くこと(mod_perl2 or SpeedyCGI)

の優先度で作成されています。

*4 : とくにプログラムを学んだのではなくプログラムを教えてもらった人間に多い

*5 : メンテするぐらいなら最初から書き直した方がよっぽど速いし

*6 : 美しく書けば雑然と書くよりは普通に速いので、よっぽど速度重視な場面以外ではあまり構わない

*7 : ワンパスしか処理しないような場所で不必要な最適化をするとか