Tips ちょっと気がついたこと


●3. r+でファイルをオープン
ずいぶんと久しぶりにHPを更新しています。
トップページにも書いてありますがHP大改革計画中。

最近やらかしたRubyでのおおぽか。

ファイルのオープン時に

open(file, "r+") do |fh|
	file_data = fh.read
	file_data = file_data.sub(/#{source_str}/, changed_str)
	fh.rewind
	fh.write file_data
end

こんなことをやっていたのです。
期待としてはファイルを読み込んで、変換して、新しいファイルに書き換える。
でも、新しいファイルが元よりも小さい場合に、どうもゴミが残るんです。
よく考えたら、rewindしてもファイルは消えないんですね。。。(苦笑)
そんなわけで、rewindの前にtruncate(0)を入れて、ファイルの中身を消すようにしました。
で直したものが以下です。

open(file, "r+") do |fh|
	file_data = fh.read
	file_data = file_data.sub(/#{source_str}/, changed_str)
	fh.rewind
	fh.write file_data
end
●2. perlとrubyの比較 ループ内の正規表現(続編)
1.の結果について正規表現ではなく
body += i
の部分がボトルネックになっているのではというご意見をいただきましたので
(body << iの方がメモリの初期化が無いので速いということで)
それを修正&比較してもう一度計測しなおしてみました。
(ちょっとプログラムが変わっていたりもします。)

--- test1.pl---
$html_file = 'index.htm';
$html_head = '<h1>HEAD</h1>';
$html_foot = '<address>FOOT</address>';

open(IN , $html_file);
while(<IN>){
	print $html_foot if(/^<\/body/);
	print $_;
	print $html_head if(/^<body/);
}
close(IN);
------

--- test2.pl ---
$html_file = 'index.htm';
$html_head = '<h1>HEAD</h1>';
$html_foot = '<address>FOOT</address>';

$indata = '';
open(IN , "$html_file");
while(<IN>){ $indata .= $_; }
close(IN);

$indata =~ s/(<\/body)/$html_foot$1/;
$indata =~ s/(<body.*?>)/$1$html_head/;
print $indata;
------

--- test11.rb ---
html_file = 'index.htm'
html_head = '<h1>HEAD</h1>'
html_foot = '<address>FOOT</address>'
body = ''

filedata = open(html_file, "r")

filedata.each do |i|
	body += html_foot if i =~ /^<\/body/
	body += i
	body += html_head if i =~ /^<body/
end
print body
------

--- test12.rb ---
html_file = 'index.htm'
html_head = '<h1>HEAD</h1>'
html_foot = '<address>FOOT</address>'
body = ''
filedata = open(html_file, "r")

filedata.each do |i|
	body << html_foot if i =~ /^<\/body/
	body << i
	body << html_head if i =~ /^<body/
end
print body
------

--- test21.rb ---
html_file = 'index.htm';
html_head = '<h1>HEAD</h1>';
html_foot = '<address>FOOT</address>';
body = ''

filein = open(html_file)
body += filein.read
filein.close

body.sub!(/(<\/body)/, "#{html_foot}#{$1}")
body.sub!(/(<body.*?>)/, "#{$1}#{html_head}")
print body
------

--- test22.rb ---
html_file = 'index.htm';
html_head = '<h1>HEAD</h1>';
html_foot = '<address>FOOT</address>';
body = ''

filein = open(html_file)
body << filein.read
filein.close

body.sub!(/(<\/body)/, "#{html_foot}#{$1}")
body.sub!(/(<body.*?>)/, "#{$1}#{html_head}")
print body
------

環境
FreeBSD2.2.8
CPU: Pentium/P54C (72.41-MHz 586-class CPU)メモリ
MEM: 48MB
扱っているindex.htmはhttp://www.hi-fi-net.com/ruby/cgi/index.htmです。


%perl -v
This is perl, version 5.005_03 built for i386-freebsd
Copyright 1987-1999, Larry Wall
Perl may be copied only under the terms of either the Artistic License or the
GNU General Public License, which may be found in the Perl 5.0 source kit.
Complete documentation for Perl, including FAQ lists, should be found on
this system using `man perl' or `perldoc perl'.  If you have access to the
Internet, point your browser at http://www.perl.com/, the Perl Home Page.


%ruby -v
ruby 1.2.6(99/06/21) [i386-freebsd2.2.8]


なんだかファイル数がやけに増えてしまいましたが結果を書きます。
%/usr/bin/time perl test1.pl > /dev/null
        0.06 real         0.05 user         0.00 sys

%/usr/bin/time perl test2.pl > /dev/null
        0.06 real         0.02 user         0.04 sys

%/usr/bin/time ruby test11.rb > /dev/null
        0.23 real         0.17 user         0.05 sys

%/usr/bin/time ruby test12.rb > /dev/null
        0.17 real         0.14 user         0.03 sys

%/usr/bin/time ruby test21.rb > /dev/null
        0.13 real         0.11 user         0.02 sys

%/usr/bin/time ruby test22.rb > /dev/null
        0.13 real         0.10 user         0.02 sys

やっぱり指摘していただいたように+=と<<の違いが見事に出ました。
まだrubyに触って大して時間が経っていないときだったとはいえなさけないです。
rubyでは変数の初期化で結構負荷がかかるので注意が必要ですね。
といっても「破壊的な」メソッドも注意しないと危険だし。

とはいえ正規表現をたくさんやるのと一回で済ますのにはかなりの差が出ました。
やはりperlとrubyの差でしょうか。

念のためにruby1.4.2での結果も載せておきます。
%/usr/bin/time ruby test12.rb > /dev/null
        0.17 real         0.14 user         0.00 sys

%/usr/bin/time ruby test22.rb > /dev/null
        0.13 real         0.09 user         0.03 sys

これは
%ruby -v
ruby 1.4.2 (1999-09-18) [i386-freebsd2.2.8]
です。
そんなに差は出ませんでした。
●1. perlとrubyの比較 ループ内の正規表現
以下のプログラムはあるhtmlファイルを読みこんで
<body>タグの後と</body>の前にある表示を入れるものです。

perlだと

---(1)---
open(IN , $html_file);
while(<IN>){
	if(/^<\/body/){
		print $html_foot;
	}
	print $_;
	if(/^<body/){
		print $html_head;
	}
}
close(IN);
------

---(2)---
$indata = '';
open(IN , "$html_file");
while(<IN>){ $indata .= $_; }
close(IN);

$indata =~ s/(<\/body)/$html_foot$1/;
$indata =~ s/(<body.*?>)/$1$html_head/;
print $indata;
------

perlだとCGIでファイルを表示させるときに1番の方がごくわずかに速く動作しました。
2回動作させた結果が

1番  9.67 real         1.76 user         3.67 sys
2番  10.18 real         2.20 user         3.33 sys

となりました。
perlではファイルを読みながら小さい文字列を何回も正規表現をかけた方が
効率が多少よかったです。

---(1)---
filedata.each do |i|
	if i =~ /^<\/body/
		body += html_foot
	end
	body += i
	if i =~ /^<body/
		body += html_head
	end
end
print body
------

---(2)---
filein = open(html_file)
body += filein.read
filein.close

body.sub!(/(<\/body)/, "#{html_foot}#{$1}")
body.sub!(/(<body.*?>)/, "#{$1}#{html_head}")
print body
------

ところがrubyでにた状況を作って動作させた結果
1番に比べて2番の方が圧倒的に速いんです。

1番  17.63 real         3.18 user         5.37 sys
2番  12.04 real         3.40 user         5.24 sys

どうもボトルネックは正規表現を何度も動作させることなようです。
実行速度は1.5倍程度ですみましたがtopで見ていたら1番はすさまじいほどにメモリを食っていました。
rubyではファイルを全部読んでおいて一度に正規表現をかけた方が圧倒的に効率がいいです。

今ふと気が思ったんですがどちらの1番もif就職詞を使った方がきれいに書けますね。


perlとrubyとは表示させたファイルがすこし違うし、動作条件も多少違うので
rubyとperlの実行速度の違いは気にしないで下さい。
もしかしたらこういう条件をきちっと揃えたらまた他の事が原因なのかもしれません。


これの動作環境はFreeBSD2.2.8に
rubyは1.2.5
perlは5.005_03
マシンはDX2-66MHzにメモリ20MB
です。
(あ! perlはバージョンアップした時に日本語パッチを当てておくのを忘れてた。)



| メニュー戻る |

LinkExchange Japan
LinkExchange Japan Member