プログラムの脳へのロード時間短縮に関する努力
0. 前書き
こんな記事を見かけた。ちょっと昔の記事だけど。
頭の中にプログラムを入れる
http://www.aoky.net/articles/paul_graham/head.htm
ハッカーと画家のポール・グレアム氏の文で、プログラマは頭の中にプログラムをロードしてからコードを書く。このロードという処理はかなり時間がかかるので、ロード回数を減らすこと、ロードの負荷を減らすことが大切だと語られている。
確かにプログラムを脳にロードするのは時間がかかる。そして一度ロードしたコードはいとも容易く頭から抜けていく。昨日書いたコードくらいならだいたい覚えていられるが、休日明けの月曜日になると前週ロードしたコードは半分以上失われてしまう。ゴールデンウィーク明けなんて綺麗にフォーマットされた状態になってしまう。他のプログラムを触って帰って来ると、短時間で自分が書いていたコードの情報が抜け落ちてしまうこともある。そのたびに時間をかけてコードを頭に入れ直す作業を行うというのは、とても効率的とは言えない。
また、ロードするだけで数分かかるようなソフトウェアが使いものにならないように、脳にロードするのに長時間かかるようなコードは使いやすいものとは言えない。だからプログラマは効率良くロードできるコードを生み出すために、いくつもの努力をする。
例えば以下のような。
頭の中にプログラムを入れる
http://www.aoky.net/articles/paul_graham/head.htm
ハッカーと画家のポール・グレアム氏の文で、プログラマは頭の中にプログラムをロードしてからコードを書く。このロードという処理はかなり時間がかかるので、ロード回数を減らすこと、ロードの負荷を減らすことが大切だと語られている。
確かにプログラムを脳にロードするのは時間がかかる。そして一度ロードしたコードはいとも容易く頭から抜けていく。昨日書いたコードくらいならだいたい覚えていられるが、休日明けの月曜日になると前週ロードしたコードは半分以上失われてしまう。ゴールデンウィーク明けなんて綺麗にフォーマットされた状態になってしまう。他のプログラムを触って帰って来ると、短時間で自分が書いていたコードの情報が抜け落ちてしまうこともある。そのたびに時間をかけてコードを頭に入れ直す作業を行うというのは、とても効率的とは言えない。
また、ロードするだけで数分かかるようなソフトウェアが使いものにならないように、脳にロードするのに長時間かかるようなコードは使いやすいものとは言えない。だからプログラマは効率良くロードできるコードを生み出すために、いくつもの努力をする。
例えば以下のような。
1. 簡略化
プログラムは短ければ短いほど、ロード時間は短くなる。但し短さだけを追求して難解な処理を盛り込むと、逆にロード時間が増えてしまうこともある。Code Golfのテクニックを大量に盛り込んだりした日には、ロードする際にかかる脳の負荷は一気に跳ね上がる。自分のレベルに合った、お行儀の良い記法で、且つ簡略な書き方を目指すとロードしやすいソースが出来上がる。
とはいえ、いつも自分のレベルに合わせて書いていては進歩がない。ちょっと背伸びした不慣れな書き方でも何度も使っていればそれが自分の身についてくる。それが行儀が悪くないものであれば、ところどころに(300行に1度くらい)挑戦的なコードを入れることも否定されるものではない。
冒頭のURL内では、スクリプト言語のメリットを指摘している箇所がある。多くのスクリプト言語はCやJavaと比べてコードを簡略化する効果がある。Cで書けば1000行かかることを、スクリプト言語は100行で終わらせることもある。
1000行のコードと100行のコードとではロード時間は10倍以上違ってくる。100行なら脳内にバッファを1つ確保してロードするだけで済むのだが、1000行まで来ると(少なくとも私は)バッファに入れて圧縮(要約)してキャッシュしてといった少し面倒な作業を脳に行わせなければロードできない。
とはいえ、いつも自分のレベルに合わせて書いていては進歩がない。ちょっと背伸びした不慣れな書き方でも何度も使っていればそれが自分の身についてくる。それが行儀が悪くないものであれば、ところどころに(300行に1度くらい)挑戦的なコードを入れることも否定されるものではない。
冒頭のURL内では、スクリプト言語のメリットを指摘している箇所がある。多くのスクリプト言語はCやJavaと比べてコードを簡略化する効果がある。Cで書けば1000行かかることを、スクリプト言語は100行で終わらせることもある。
1000行のコードと100行のコードとではロード時間は10倍以上違ってくる。100行なら脳内にバッファを1つ確保してロードするだけで済むのだが、1000行まで来ると(少なくとも私は)バッファに入れて圧縮(要約)してキャッシュしてといった少し面倒な作業を脳に行わせなければロードできない。
2. 部品化
プログラムをわかりやすく部品化しておくと、脳へのロード時間は短くなる。というのも、作業と関係のない部品はロードを省略することができるからだ。
スクリプト言語でファイルを1行ずつ読み込む記述があったとする。この処理を脳にロードする際、誰もその裏で行われている「ファイルポインタが云々」とか「バッファが云々」といったところのコードまではロードしない。もちろんその辺りは気にかけなければいけないことではあるが、わざわざロードしなくても注意を払いさえすれば済むことだ。
脳がロードし辛い複雑な処理を書く場合は、その箇所を部品化して「これはこういうものです」というコメントの1つでも貼っておけば、後でロードする機会を大幅に減らすことができるので助かる。
但し必ず同時にロードする必要があるコードが分断されていると、追いかける際の手間が逆に増えてしまう場合もある。「なぜ、ここを分けるかなぁ。逆に読みづらいじゃないか」と思われる場合は、一緒にいた方がロードしやすい箇所を分断してしまっているのだろう。大まかな流れは一望でき、詳細な処理は細分化されているという書き方が個人的には読みやすいと思っている。
スクリプト言語でファイルを1行ずつ読み込む記述があったとする。この処理を脳にロードする際、誰もその裏で行われている「ファイルポインタが云々」とか「バッファが云々」といったところのコードまではロードしない。もちろんその辺りは気にかけなければいけないことではあるが、わざわざロードしなくても注意を払いさえすれば済むことだ。
脳がロードし辛い複雑な処理を書く場合は、その箇所を部品化して「これはこういうものです」というコメントの1つでも貼っておけば、後でロードする機会を大幅に減らすことができるので助かる。
但し必ず同時にロードする必要があるコードが分断されていると、追いかける際の手間が逆に増えてしまう場合もある。「なぜ、ここを分けるかなぁ。逆に読みづらいじゃないか」と思われる場合は、一緒にいた方がロードしやすい箇所を分断してしまっているのだろう。大まかな流れは一望でき、詳細な処理は細分化されているという書き方が個人的には読みやすいと思っている。
3. パターン化
何度も書いた経験があるパターン化されたコードは、ロードするのも楽になる。いわゆる慣れというヤツだ。また、すべてのコードがある種のルールに沿って書かれていれば、脳があらかじめそのルールを前提として備えることができるので、何の予備知識持たずに読むコードよりもロードしやすくなる。
デザインパターンは、このパターン化という面で高い効果を持つ。クラス名の末尾にFacadeとかFactoryと書いておくだけで、もしくはコメントにFlyweightとかSingletonと一言書いておくだけで、プログラマはコードの大枠を想定することができる。チームで開発する際にフレームワークを利用するのも、このパターン化の効果を狙っている部分がある。
コーディング規約にもパターン化の作用がある。例えばクラス変数の冒頭にはアンダースコアを付けるといったルールは、コードをロードする際に「この変数はなんだ?」という問題を解決するのをスムーズにする効果がある。大文字小文字の使い分け。インデントのルール。そういった些細なことが統一されているだけで、コードのロードしやすさは変わってくる。
デザインパターンは、このパターン化という面で高い効果を持つ。クラス名の末尾にFacadeとかFactoryと書いておくだけで、もしくはコメントにFlyweightとかSingletonと一言書いておくだけで、プログラマはコードの大枠を想定することができる。チームで開発する際にフレームワークを利用するのも、このパターン化の効果を狙っている部分がある。
コーディング規約にもパターン化の作用がある。例えばクラス変数の冒頭にはアンダースコアを付けるといったルールは、コードをロードする際に「この変数はなんだ?」という問題を解決するのをスムーズにする効果がある。大文字小文字の使い分け。インデントのルール。そういった些細なことが統一されているだけで、コードのロードしやすさは変わってくる。
4. 名前
名前はプログラムをロードする上で非常に重要だ。「a=0, b=0;」という記述があった場合、aとbの役割を想像するのはかなり厄介なことだ。それに対して「pageCount=0, pageStatus=0;」と書いてあれば変数の意味がそれなりに想像でき、コードを見て確認する際にも最初からアタリを付けておくことができる。
アタリを付けることの効果は非常に高い。まったく意味が分からない変数の意味を探る時は脳に総当たり探索を実行させなければいけないが、アタリが付いていれば確証を取るだけの判定処理で済む。この2つのロジックの負荷は、プログラムで言えば10ミリ秒vs10ナノ秒くらいの差がある。
全然関係のない話だが、スティーブ・ジョブズは昔アタリの社員だったということを最近になって知った。アラン・ケイもそうだったとか。へー。
話戻して、最近では命名規則をプログラミングの文法に取り入れるケースも多い。Rubyでは大文字アルファベットで始まる変数は定数として扱われるし、Pythonではアンダースコアを先頭に付けることでprivateを表す。これらは名前から機能を類推できるのでロードしやすい表現だと言える。
アタリを付けることの効果は非常に高い。まったく意味が分からない変数の意味を探る時は脳に総当たり探索を実行させなければいけないが、アタリが付いていれば確証を取るだけの判定処理で済む。この2つのロジックの負荷は、プログラムで言えば10ミリ秒vs10ナノ秒くらいの差がある。
全然関係のない話だが、スティーブ・ジョブズは昔アタリの社員だったということを最近になって知った。アラン・ケイもそうだったとか。へー。
話戻して、最近では命名規則をプログラミングの文法に取り入れるケースも多い。Rubyでは大文字アルファベットで始まる変数は定数として扱われるし、Pythonではアンダースコアを先頭に付けることでprivateを表す。これらは名前から機能を類推できるのでロードしやすい表現だと言える。
5. コメント付け
コメントはSQLのヒント文のようなものだ。ロードする上でのヒントをプログラマに提示してくれる。問題のある内容を記述してしまうと、逆にロードに時間を増やすことになりかねないあたりもヒント文と似ている。
たとえばうだうだと長いSQLが書かれていたとする。数ヶ月前に書いたもので、どういうSQLなのか内容をすっかり忘れてしまった。「このテーブルとこのテーブルがJOINして、最終的にSELECTしているのがこの値だから……」と追っていってもロードは可能だが、その前に「ユーザに紐づくスコアをスコアテーブルとユーザアクションテーブルから集計してその上位100人を出力する」といった記述がされていれば、「あー、あそこで使っているSQLか」と記憶を蘇らせるのに役立ってくれるかもしれない。「ユーザTOP100を出力する」という短い記述でも十分にヒントになる。
またコメントは、コードを読んだだけではすぐには伝わらない箇所や、知らずに使うと罠に引っかかる恐れがある箇所などで効果を発揮する。
例えば「このクラスはスレッドセーフではありません」という一言。このコメントが一言入っているだけで、スレッドセーフという判断するのに時間がかかる判定を即座にこなすことができる。もちろんそのソースの出所によってはコメント自体がフェイクだったりする場合もあるが。
プログラムを書いていると罠を仕込んでしまうこともままある。こういう引数でも動くだろうと思わせるような容姿をしていながら、その引数を入れるとエラーになるような。そうした罠がある場合はコメントに「こういう場合は対応していない」と一言書いておくと、将来そのソースを触ることになる誰か(自分自身かもしれない)がハマることを防いでくれる。
たとえばうだうだと長いSQLが書かれていたとする。数ヶ月前に書いたもので、どういうSQLなのか内容をすっかり忘れてしまった。「このテーブルとこのテーブルがJOINして、最終的にSELECTしているのがこの値だから……」と追っていってもロードは可能だが、その前に「ユーザに紐づくスコアをスコアテーブルとユーザアクションテーブルから集計してその上位100人を出力する」といった記述がされていれば、「あー、あそこで使っているSQLか」と記憶を蘇らせるのに役立ってくれるかもしれない。「ユーザTOP100を出力する」という短い記述でも十分にヒントになる。
またコメントは、コードを読んだだけではすぐには伝わらない箇所や、知らずに使うと罠に引っかかる恐れがある箇所などで効果を発揮する。
例えば「このクラスはスレッドセーフではありません」という一言。このコメントが一言入っているだけで、スレッドセーフという判断するのに時間がかかる判定を即座にこなすことができる。もちろんそのソースの出所によってはコメント自体がフェイクだったりする場合もあるが。
プログラムを書いていると罠を仕込んでしまうこともままある。こういう引数でも動くだろうと思わせるような容姿をしていながら、その引数を入れるとエラーになるような。そうした罠がある場合はコメントに「こういう場合は対応していない」と一言書いておくと、将来そのソースを触ることになる誰か(自分自身かもしれない)がハマることを防いでくれる。
6. ブロック化
200行の処理があったとする。一連の処理であり、分割すると逆に見づらくなる。しかし何の工夫もなく真っ直ぐに記述すると少しロードし辛いコードになってしまう。そんな時はコードが視覚的にブロック分けされているかのように見えるようコメントで区切りを入れたり、ところどころ多めに改行を入れることがある。
一度に脳にロードできるコードの量は(私の場合は)5行から10行程度。余程読みやすい箇所でも20行。かなり少ない単位でしかロードすることはできない。ロードすべき単位ごとにコードが視覚的にブロック分けされていれば、脳内で次にどこまでバッファリングするべきかを判断する手間が省ける。
これは日本語における句読点や改行に近い。最近の小説の改行の多さや文字の大きさはどうかと思うけど、少なくともプログラムを書く上では三島由紀夫のような流暢な記述ではなく、改行と簡易な表現が多様されるライトノベルのようなコーディングを心がけると良いのかもしれない。
ところで最近の小説の活字はなんであんなに大きいのだろう。あそこまで大きいと逆に読みづらい。
一度に脳にロードできるコードの量は(私の場合は)5行から10行程度。余程読みやすい箇所でも20行。かなり少ない単位でしかロードすることはできない。ロードすべき単位ごとにコードが視覚的にブロック分けされていれば、脳内で次にどこまでバッファリングするべきかを判断する手間が省ける。
これは日本語における句読点や改行に近い。最近の小説の改行の多さや文字の大きさはどうかと思うけど、少なくともプログラムを書く上では三島由紀夫のような流暢な記述ではなく、改行と簡易な表現が多様されるライトノベルのようなコーディングを心がけると良いのかもしれない。
ところで最近の小説の活字はなんであんなに大きいのだろう。あそこまで大きいと逆に読みづらい。
7. ドキュメント化
ドキュメントはコードの全体像をプログラマの脳にロードさせる用途で特に役に立つ。全体像がロードされれば、その後、詳細を追う際に「全体の中のこの辺りではないだろうか」と想定しながらコードを読むことができる。逆に全貌がまったく分かっていないプログラムについて、コードから流れを把握するのはとても難しい。
JavaDocは偉大だ。JavaDocで生成されたクラスとパッケージ一覧を見て、気になったクラスのメソッド一覧を見ればそれだけでソースの全体像が想像できる(クラスやメソッドにまともな命名されてさえいればという条件付きだが)。RubyにはRDocが、Pythonにはpydocがいることからも、この手のドキュメント自動生成ツールの有用性が伝わってくる。
スキーマからER図を生成したり、コードからクラス図やシーケンス図を生成するツールも存在する。これらはプログラマの手を煩わせることなく全体を把握するための有効なヒントをもたらしてくれる。
手書きで書かれたドキュメントももちろん役に立つ。自動生成ドキュメントよりもしっかりと書かれていることが多く、プログラムをロードするのに必要なヒントを大量に得ることが期待できる。ただ、こちらは作成するのに時間がかかる上に、最新の状態が保たれている保証がない。コードを変えるたびにドキュメントを適切に修正するのは時間がかかり過ぎる上に、漏れなく行うのは困難だ。さらに大きなデメリットとして、ドキュメントを書くことはプログラマのモチベーションを大きく下げる。
JavaDocは偉大だ。JavaDocで生成されたクラスとパッケージ一覧を見て、気になったクラスのメソッド一覧を見ればそれだけでソースの全体像が想像できる(クラスやメソッドにまともな命名されてさえいればという条件付きだが)。RubyにはRDocが、Pythonにはpydocがいることからも、この手のドキュメント自動生成ツールの有用性が伝わってくる。
スキーマからER図を生成したり、コードからクラス図やシーケンス図を生成するツールも存在する。これらはプログラマの手を煩わせることなく全体を把握するための有効なヒントをもたらしてくれる。
手書きで書かれたドキュメントももちろん役に立つ。自動生成ドキュメントよりもしっかりと書かれていることが多く、プログラムをロードするのに必要なヒントを大量に得ることが期待できる。ただ、こちらは作成するのに時間がかかる上に、最新の状態が保たれている保証がない。コードを変えるたびにドキュメントを適切に修正するのは時間がかかり過ぎる上に、漏れなく行うのは困難だ。さらに大きなデメリットとして、ドキュメントを書くことはプログラマのモチベーションを大きく下げる。
8. 哲学
良いコードは「これを書いた人なら、この処理はきっとこう記述するだろう」という想像を読み手の心に抱かせる。コードに明確なパターンがあるわけではなく、特別な規約があるわけでもない。良いコードは人間の脳の得意分野である「推測する能力」を不思議と快適に働かせることができるのだ。そしてそういったコードを見ると人は「このコードに哲学がある」とか「思想がある」と感じる。
聞くたびに話が変わる上司の命令をこなすのが困難なように、行を移るたびにこれまでとは違った書き方がされているコードは読みづらい。人の推測する能力が働くような一貫性を持ったコードは、ロードする際の脳への負荷を減らしてくれる。人の脳は類推した通りの事象を目の前にする方が好きらしい。
では、どうやれば哲学のあるコードを書けるようになるのか。手っ取り早い方法は、たくさんの良いコードを読んで「これは好きだ」「これは嫌いだ」という判定をしていくことだと思う。同じように優れたコードでも、人によって好き嫌いは分かれる。「これは確かに良いコードだけど、今ひとつ好きになれない」と思うこともしばしばある。そうした好き嫌いの判断を繰り返していけば自分が好きなコードの方向が明確になっていき、自然と自身が書くコードにも一貫した方向性が出てくるのではないだろうか。
聞くたびに話が変わる上司の命令をこなすのが困難なように、行を移るたびにこれまでとは違った書き方がされているコードは読みづらい。人の推測する能力が働くような一貫性を持ったコードは、ロードする際の脳への負荷を減らしてくれる。人の脳は類推した通りの事象を目の前にする方が好きらしい。
では、どうやれば哲学のあるコードを書けるようになるのか。手っ取り早い方法は、たくさんの良いコードを読んで「これは好きだ」「これは嫌いだ」という判定をしていくことだと思う。同じように優れたコードでも、人によって好き嫌いは分かれる。「これは確かに良いコードだけど、今ひとつ好きになれない」と思うこともしばしばある。そうした好き嫌いの判断を繰り返していけば自分が好きなコードの方向が明確になっていき、自然と自身が書くコードにも一貫した方向性が出てくるのではないだろうか。
9. 後書き
こうした視点で見ていくと多くのプログラミングに関する技術は「プログラムを頭に入れる際の脳への負荷」を軽減する役割があることが分かる。
そうした技法が次々に生み出されている割には、プログラマの勤務体系の効率化、ミーティングタイミングの適正化などによるロード時間軽減の技法を目にする機会があまりないのは不思議なことだと思う。
そうした技法が次々に生み出されている割には、プログラマの勤務体系の効率化、ミーティングタイミングの適正化などによるロード時間軽減の技法を目にする機会があまりないのは不思議なことだと思う。
10. 修正履歴
- 2010/02/09 19:30 : 誤字、表現が分かりづらい箇所をちょこちょこ修正