ミケネコ研究所 / プログラミング研究室 / Perl チューニング

わかりやすい Perl チューニングの話

Perl の内部構造とパフォーマンス・チューニング

Last modified: 2001/09/24

このページの目次



はじめに

この文書は、Perl の内部構造を知り適切なプログラム設計およびパフォーマンス・チューニングを行えるようになることを目的としています。読者は「プログラミング Perl」(通称青らくだ本)の所持を前提としています。

らくだ本「8.3 効率の問題」の項は興味深く、時間効率・空間効率に関するいくつかのヒントが載っていますので、是非読んでみてください。しかしそこから Perl の内部挙動を読み取ることは至難の業です。この連載では、もう少し Perl の内部に立ち入ってみたいと思います。

第 1 回 メモリ割り当て

まずは変数に文字列を代入してみましょう。

$foo = "abc";

この時点で、メモリ内部では、

 ■■■
 a b c

と格納されています。このように代入した文字数だけメモリが確保され、文字が格納されます。この状態から、文字列を拡張すると何が起こるでしょうか?

$foo .= "def";
 ■■■■■■
 a b c d e f

予想どおりの結果です。Perl は 3 文字分のメモリを新しく拡張しました。さて、ここで末尾3文字を縮めると何が起こるでしょうか?

substr ($foo, 3, 3) = ""; # 末尾 3 文字を縮める
または
$foo =~ s/def//;          # 末尾 3 文字を縮める
 ■■■□□□
 a b c 

このように、$foo の中身は abc となりましたが、メモリ空間は 6 文字ぶんを保持したままです。さらに縮めてみましょう。

$foo = "";
 □□□□□□

変数の中身はからっぽになってしまいましたが、$foo を undef しない限りは依然としてメモリ空間を 6 文字ぶん保持しています。

らくだ本には、『文字列をあらかじめ必要な長さに拡張しておけば、ある程度時間を節約することができる。文字列なら x 演算子を使うことによってあらかじめ十分な大きさに拡張しておけば、拡張する際のオーバーヘッドを避けることができる。また、メモリの断片化も最小にすることが出来る』という記述があります。

たとえば $foo .= "bar"; を多く繰り返す作業があるときに、あらかじめ $foo を必要な長さだけ拡張しておけば、メモリ空間の拡張は最初の1度だけで済むのです。例:

$foo = "a" x 100000;      # 100000バイトほどに拡張する。
$foo = "";                # 中身を空っぽにする(しかしメモリ空間は確保されたまま)

$foo に対する作業...

この前処理の2行を加えることによって、メモリ拡張は1度だけしか行われません。「メモリ割り当て」にかかる時間が節約されたわけです

補足 実際のところ、こんな前処理をすることによって節約できる時間はそれほど多くありません。しかし、変数とメモリ空間の仕組みを知ることは重要です。

第 2 回 値のコピー

文字列の拡張の際に Perl がどんな挙動を示すかを詳しく見ていきましょう。まずは $foo のメモリの状態が、

 ■■■■■■
 a b c d e f

であるとき、$foo に 1 文字挿入してみることを考えてみましょう。

  1. $foo の先頭に "Z" を挿入する。例: substr ($foo, 0, 0) = "Z";
  2. $foo の中間に "Z" を挿入する。例: substr ($foo, 4, 0) = "Z";
  3. $foo の末尾に "Z" を挿入する。例: substr ($foo, 6, 0) = "Z";

1〜3 のどれをやっても、Perl はメモリ空間を 1 文字ぶん拡張し、メモリ空間も $foo の正味の長さも 7 文字ぶんになるという点では同じです。しかし、内部の挙動はだんぜん違います。末尾への挿入がもっとも速く行われ、先頭への挿入はもっとも遅いのです。なぜでしょうか?

末尾に挿入するときの、メモリ内部の挙動を見てみましょう。

1: $foo = "abcdef";
2: substr ($foo, 6, 0) = "Z";
line 1:
 ■■■■■■
 a b c d e f

line 2:
 ■■■■■■□
 a b c d e f     メモリ空間を 1 文字分拡張

 ■■■■■■■
 a b c d e f Z   Z を格納

これと比べて、中間に挿入することを考えてみましょう。

1: $foo = "abcdef";
2: substr ($foo, 2, 0) = "Z";
line 1:
 ■■■■■■
 a b c d e f

line 2:
 ■■□□□□
 a b             cdef は退避する。

 ■■□□□□□
 a b             メモリ空間を 1 文字分拡張

 ■■■□□□□
 a b Z           挿入する。

 ■■■■■■■
 a b Z c d e f   cdef をコピーしてくる

中間や先頭に挿入する際には、メモリ内部で「値のコピー」が発生するために、末尾への挿入よりオーバーヘッドがかかっています。文字列が長いとき、これはやや深刻です。

変数への操作が頻繁であるとき、第 1 回の「メモリ割り当て」と、第 2 回の「値のコピー」、この両者ができるだけ少なくなるようなアプローチの選択が、パフォーマンスの改善につながることでしょう。


次回からは、スカラー変数の伸張の際に詳しい挙動について、書きたいと思います。


このページについて


このページに書いてある情報の正しさを保証しません。この文章を読んだことで生じる責任は、読者自身にあるものとします。

このページはリンク自由です。必ずしもTOPへのリンクでなくてもいいですし、連絡も不要です。

2001/09/24
第 1 回と第 2 回 を執筆。

You are th visitor since 2001/09/24. Thank you.

♪ ♪ 御意見帳フォーム ♪ ♪
このサイトのご意見をお聞かせください。匿名でも結構です。お気軽にどうぞ。

ボタンを押すと、ミケネコ研究所宛に投書したあと、自動的にこのページに戻ります。
(これは、メール送信ではありません。あなたのメールアドレスを必要としません。)

わかりやすい Perl チューニングの話 http://mikeneko.creator.club.ne.jp/~lab/perl/tuning/