Tweet

Linux/misc

前提

話の前提として,以下のような状態を考える.

  • 複数人でのプログラム開発
  • メンバーのスキルは様々
    • メンバーのスキルアップ,という手段は考慮しない.*1
  • 仕様通りのプログラムの完成が第一目標

このような条件で

いかにプログラムの品質を一定レベル以上に保つか

というあたりを考えてみる. 「スキルの高い人間が効率よくプログラミングできる」という議論でないことに注意.

環境

Linux のシェルプロンプトから実行可能な言語とする. つまり,Visual Basic や JavaScript は対象外とする.

速度

プログラムの実行速度は,もちろん速いにこしたことはない. 「実行速度が遅い」ということで仕様を満たさないプログラムは存在しうるが,「速過ぎる」ことが大問題となることはない. 速過ぎる場合はウェイトを入れれば済むだけの話なのだから.

あと,実行速度が遅いために,変にコードの最適化を行わなければならない場合もあるだろう. 最適化によりプログラムのコード量も増え,見通しも悪くなり,メンテナンス性も悪くなる.

それから「論理的には間違っていないが効率の悪いプログラム」も考慮する必要がある.*2 議論の対象は,トップレベルのプログラマが書いたプログラムではなく,低いスキルのプログラマの書いたプログラムを救うことにあるのだから.

つまり,言語の実行速度については

速さこそ正義

ということが言えるだろう.*3

速度に関しては,perl, ruby, python のようなインタプリタ型の言語ではなく,C, C++ などのコンパイラ型言語のほうが有利であろう.

プロファイラ

もし,実行速度に不満のある場合,お世話になるのがプロファイラである. プロファイリングを有効にしたプログラムを実行すると,ルーチンごとの実行回数・実行時間の統計を取ることができる.

プロファイラが無い場合,「ここが実行に時間かかっていそうだ」というあたりをヤマカンで高速化していくしかない. が,「80-20の法則」*4と言われるように,プログラム内で CPU 時間を食っているのはほんの一部分である. この「20 %」にヒットするまでプログラムを書き換えていくのは効率が悪い. また,ほとんど関係なかった部分を元に戻す,というのもこれまた大変な作業である.

つまり

プロファイラはあったほうがいい

ということである.

文字列

文字列の取り扱いというのは,非常に重要な問題であろう.

バッファオーバフロー,というのは,サーバへの攻撃手段としてはもはや「古典的」ともいえる手段であろう. サーバが想定していた文字数以上の文字を与え,スタック領域を書き換えて自分の送り込んだプログラムへジャンプさせる,という手法である. sendmail などの有名なプログラムもこのような問題を抱えていたことがある.

sendmail は C で記述されている. つまり,C で文字列領域を徹頭徹尾完璧に管理するのは,一流のプログラマでも難しい課題だと言えるだろう.

これに対し,perl, ruby などの言語の文字列は言語環境側で管理されているので,バッファオーバフローのように「確保した領域をはみ出して文字列が格納されてしまう」ということはない.

C++ にも,領域管理のある string クラスがあるが,C 流の char[] によるトラッドな文字列処理も記述できてしまう. この点に関して C++ に対する評価は微妙なところであろう.

ヒープ管理

要するに C でいうところの malloc(3), free(3) である.

原則として malloc() した領域は必ず free() で解放する必要がある. でないと,malloc() したもののポインタ変数が消滅*5してしまい,どのポインタからも参照できないヒープ領域が残ってしまう. いわゆるメモリリークである.

メモリリークは短時間で終了してしまうプログラムなら問題とはならないが,daemon のように長時間常駐するプログラムの場合は問題となる. electric fence などのデバッグツールがあることからしても,この問題が一般的なものであることがわかるだろう.

ここで「なぜ malloc() を呼び出すのか」ということについて考えてみる. malloc() で確保した領域になんらかのデータを格納したいからである. ということは,オブジェクト指向言語においては,malloc()/free() は,「何らかのデータ」オブジェクトの生成/解放に置き換えることができる. このとき,free() に相当する処理は,そのオブジェクトが参照されなくなった時点で自動的に呼び出される. つまり,ヒープの解放処理の抜け,というのは生じないことになる.

ガベージコレクション

オブジェクト指向言語の処理系で,参照されなくなったヒープ領域を解放するのが「ガベージコレクション」という処理である. で,現在メジャーな言語では,主に以下のようなアルゴリズムが使用されている.*6

reference counting

各オブジェクトの「参照されている数」を保持し,その数が 0 となったときに領域を解放する. 処理速度は速いが,a → b → c → a のような循環参照は,プログラム中のどの変数からも参照されなくなったとしても「参照されている数」が 0 とはならず,領域が解放されない.

循環参照された領域を解放するには,どれかのリファレンスに null を代入し,輪を断ち切ってやる必要がある.

perl はこのアルゴリズムを使用しているため,このような参照構造を利用する場合は,デストラクタに工夫が必要である.

mark and sweep

現在有効な変数からリファレンスをたどって,到達可能なオブジェクトに印を付け (mark),印の付いていないオブジェクトを解放する(sweep). この場合,reference counting で問題となった循環参照しているオブジェクトであっても解放される.


*1 実現困難な場合が多い
*2 もちろん,論理的に間違っているプログラムは対象外である
*3 プログラムの速度,でないことに注意
*4 CPU 時間の 80 % を占めているのは,プログラム内の 20 % である,というように,大部分の CPU 時間を占めているのはプログラム中のほんの一部分である,という経験則
*5 自動変数など
*6 http://www.is.s.u-tokyo.ac.jp/~vu/jugyo/processor/process/soft/compilerresume/gc/gc.html

トップ   編集 凍結 差分 バックアップ 添付 複製 名前変更 リロード   新規 一覧 単語検索 最終更新   ヘルプ   最終更新のRSS
Last-modified: 2006-02-03 (金) 06:26:29 (4329d)