2011年6月24日

[Namazu-devel-ja 1790]大量ファイルでのmknmzのメモリ不足について

青柳です。

手元のテストデータ(約200万ファイル)を一度にmknmzに掛けようとすると
メモリ不足で強制終了してしまいました。調べてみますと、find_target()の
中で終了しているようです。

find_target()ではインデックス対象ファイル名を @flist に全て保持して
いますので、これが対象ファイルが多くなった場合にメモリ不足を起こしている

原因ではないかと考えます。チェックポイントの前ですので -s オプションは
この場合は有効ではありません。ちなみに、Devel::Size で @flist のメモリ
サイズを確認したところ、160MBほどになっていました。

解決方法としては、理想的には @flist についても $ON_MEMORY_MAX の値を
適用して、越えるようなら適時ファイルに書き出して処理するようにする
ことではないかと考えますが、それは修正箇所が多くなって大変です。

そこで、解決とまではいかなくても多少の改善くらいは出来ないかとクイック
ハックを試みましたのでご報告いたします。今回試みましたのは次の2点です。

 1. ワーク変数の使用後の解放
 2. @flistのリファレンス渡し


1. ワーク変数の使用後の解放

find_target()では @flist に対して uniq や sort などの処理を行うときに
ワーク変数を使用しています。@flist が巨大になると、これらのワーク変数も
巨大になります。

この変数が、使用後も find_target() の終了まで保持されていることで
メモリが余分に使用されていると考え、使用後すぐに解放するように
修正してみました。


2. @flistのリファレンス渡し

@flist は find_target() から return するときに配列渡しになっています。
配列渡しの場合、配列のコピーが行われますからメモリの使用量は単純に
2倍になってしまいます。これをリファレンス渡しとすることでメモリの
使用量を削減することを試みてみました。


オリジナル版とこれらの修正を行った版とで、50万件に減らしたテストデータで
実行し、prep()の終了直前でpsコマンドを呼び出してメモリ使用量を計測したところ

> ./mknmz -O testindex/ data1
Looking for indexing files...
USER PID %CPU %MEM VSZ RSS TT STAT STARTED TIME COMMAND
aoyagi 15667 87.0 59.1 346452 302296 0 S+ 3:59午後 0:19.20 /usr/bin/perl5 -


> ./mknmz.fewmem -O testindex/ data1
Looking for indexing files...
USER PID %CPU %MEM VSZ RSS TT STAT STARTED TIME COMMAND
aoyagi 15672 89.0 54.9 310612 280800 0 S+ 3:59午後 0:21.10 /usr/bin/perl5 -

となりましたので、わずかながらですが効果はあるようです。

残念ながら元々の200万件のデータはこの修正を行っても処理できません
でしたが、スワップを大量に追加してやることでなんとか処理できました。

*** mknmz 2011-06-24 14:56:01.000000000 +0000
--- mknmz.fewmem 2011-06-24 14:56:24.000000000 +0000
***************
*** 910,916 ****
my $docid_base = 0;
my $output_dir = shift @_ ;
my @targets = @_ ;
! my @flist = ();

$var::OUTPUT_DIR = $output_dir;

--- 910,916 ----
my $docid_base = 0;
my $output_dir = shift @_ ;
my @targets = @_ ;
! my $flist_ref = 0;

$var::OUTPUT_DIR = $output_dir;

***************
*** 923,948 ****

check_lockfile($var::NMZ{'lock2'});
print _("Looking for indexing files...\n");
! @flist = find_target(@targets);
! ($docid_base, @flist) = append_index(@flist)
if -f $var::NMZ{'r'};
! unless (@flist) { # if @flist is empty
print _("No files to index.\n");
exit 0;
}
set_lockfile($var::NMZ{'lock2'});
! save_flist(@flist);
! my $total_files_num = @flist;

return ($docid_base, $total_files_num);
}

! sub save_flist(@) {
! my @flist = @_;
! return if (@flist == 0);

my $fh_flist = util::efopen(">$var::NMZ{'_flist'}");
! print $fh_flist join("\n", @flist), "\n";
util::fclose($fh_flist);
}

--- 923,948 ----

check_lockfile($var::NMZ{'lock2'});
print _("Looking for indexing files...\n");
! $flist_ref = find_target(@targets);
! ($docid_base, $flist_ref) = append_index($flist_ref)
if -f $var::NMZ{'r'};
! unless (@$flist_ref) { # if @$flist_ref is empty
print _("No files to index.\n");
exit 0;
}
set_lockfile($var::NMZ{'lock2'});
! save_flist($flist_ref);
! my $total_files_num = @$flist_ref;

return ($docid_base, $total_files_num);
}

! sub save_flist($) {
! my $flist_ref = shift @_;
! return if (@$flist_ref == 0);

my $fh_flist = util::efopen(">$var::NMZ{'_flist'}");
! print $fh_flist join("\n", @$flist_ref), "\n";
util::fclose($fh_flist);
}

***************
*** 1455,1460 ****
--- 1455,1461 ----
# uniq @flist
my %mark = ();
@flist = grep {$mark{$_}++; $mark{$_} == 1} @flist;
+ %mark = ();

# Sort file names with consideration for numbers.
@flist = map { $_->[0] }
***************
*** 1468,1474 ****
# For --verbose option.
report_find_target($elapsed, $#flist + 1, %counts);

! return @flist;
}

sub add_target ($\@\%) {
--- 1469,1475 ----
# For --verbose option.
report_find_target($elapsed, $#flist + 1, %counts);

! return \@flist;
}

sub add_target ($\@\%) {
***************
*** 1758,1770 ****
#
# Preparation processing for appending index files.
#
! sub append_index (@) {
! my @flist = @_;

my $docid_base = 0;
! ($docid_base, @flist) = set_target_files(@flist);

! unless (@flist) { # if @flist is empty
if ($DeletedFilesCount > 0 || $UpdatedFilesCount > 0) {
set_lockfile($var::NMZ{'lock2'});
update_dateindex();
--- 1759,1771 ----
#
# Preparation processing for appending index files.
#
! sub append_index ($) {
! my $flist_ref = shift @_;

my $docid_base = 0;
! ($docid_base, $flist_ref) = set_target_files($flist_ref);

! unless (@$flist_ref) { # if @$flist_ref is empty
if ($DeletedFilesCount > 0 || $UpdatedFilesCount > 0) {
set_lockfile($var::NMZ{'lock2'});
update_dateindex();
***************
*** 1787,1793 ****
copy($var::NMZ{'p'}, $var::NMZ{'_p'});
copy($var::NMZ{'pi'}, $var::NMZ{'_pi'});

! return ($docid_base, @flist);
}

#
--- 1788,1794 ----
copy($var::NMZ{'p'}, $var::NMZ{'_p'});
copy($var::NMZ{'pi'}, $var::NMZ{'_pi'});

! return ($docid_base, $flist_ref);
}

#
***************
*** 1795,1801 ****
#
sub set_target_files() {
my %rdocs; # 'rdocs' means 'registered documents'
! my @found_files = @_;

# Load the list of registered documents
$rdocs{'name'} = load_registry();
--- 1796,1802 ----
#
sub set_target_files() {
my %rdocs; # 'rdocs' means 'registered documents'
! my $found_files_ref = shift @_;

# Load the list of registered documents
$rdocs{'name'} = load_registry();
***************
*** 1805,1827 ****
my @overlapped_files;
grep {$_ !~ /^\# / && $mark1{$_}++ } @{$rdocs{'name'}};
$rdocs{'overlapped'} = {}; # Prepare an anonymous hash.
! for my $overlapped (grep { $mark1{$_} } @found_files) {
$rdocs{'overlapped'}{$overlapped} = 1;
push @overlapped_files, $overlapped;
};

# Pick up not overlapped documents which are files to index.
! my @flist = grep { ! $mark1{$_} } @found_files;

if ($var::Opt{'noupdate'}) {
! return (scalar @{$rdocs{'name'}}, @flist);
};

# Load the date index.
$rdocs{'mtime'} = load_dateindex();

if (@{$rdocs{'mtime'}} == 0) {
! return (scalar @{$rdocs{'name'}}, @flist);
};

util::assert(@{$rdocs{'name'}} == @{$rdocs{'mtime'}},
--- 1806,1829 ----
my @overlapped_files;
grep {$_ !~ /^\# / && $mark1{$_}++ } @{$rdocs{'name'}};
$rdocs{'overlapped'} = {}; # Prepare an anonymous hash.
! for my $overlapped (grep { $mark1{$_} } @$found_files_ref) {
$rdocs{'overlapped'}{$overlapped} = 1;
push @overlapped_files, $overlapped;
};

# Pick up not overlapped documents which are files to index.
! my @flist = grep { ! $mark1{$_} } @$found_files_ref;
! %mark1 = ();

if ($var::Opt{'noupdate'}) {
! return (scalar @{$rdocs{'name'}}, \@flist);
};

# Load the date index.
$rdocs{'mtime'} = load_dateindex();

if (@{$rdocs{'mtime'}} == 0) {
! return (scalar @{$rdocs{'name'}}, \@flist);
};

util::assert(@{$rdocs{'name'}} == @{$rdocs{'mtime'}},
***************
*** 1832,1838 ****
my @deleted_documents;
unless ($var::Opt{'nodelete'}) {
my %mark2;
! grep { $mark2{$_}++ } @found_files;
for my $deleted (grep { $_ !~ /^\# / && ! $mark2{$_} &&
! $rdocs{'overlapped'}{$_} }
@{$rdocs{'name'}})
--- 1834,1840 ----
my @deleted_documents;
unless ($var::Opt{'nodelete'}) {
my %mark2;
! grep { $mark2{$_}++ } @$found_files_ref;
for my $deleted (grep { $_ !~ /^\# / && ! $mark2{$_} &&
! $rdocs{'overlapped'}{$_} }
@{$rdocs{'name'}})
***************
*** 1858,1865 ****
# Remove duplicates.
my %seen = ();
@flist = grep { ! $seen{$_}++ } @flist;

! util::dprint(_("\n\n== found files ==\n"), join("\n", @found_files), "\n");
util::dprint(_("\n\n== registered documents ==\n"), join("\n", @{$rdocs{'name'}}), "\n");
util::dprint(_("\n\n== overlapped documents ==\n"), join("\n", @overlapped_files), "\n");
util::dprint(_("\n\n== deleted documents ==\n"), join("\n", @deleted_documents), "\n");
--- 1860,1868 ----
# Remove duplicates.
my %seen = ();
@flist = grep { ! $seen{$_}++ } @flist;
+ %seen = ();

! util::dprint(_("\n\n== found files ==\n"), join("\n", @$found_files_ref), "\n");
util::dprint(_("\n\n== registered documents ==\n"), join("\n", @{$rdocs{'name'}}), "\n");
util::dprint(_("\n\n== overlapped documents ==\n"), join("\n", @overlapped_files), "\n");
util::dprint(_("\n\n== deleted documents ==\n"), join("\n", @deleted_documents), "\n");
***************
*** 1879,1885 ****
}

# Return the number of registered documents and list of files to index.
! return (scalar @{$rdocs{'name'}}, @flist);
}

sub preupdate_registry(@) {
--- 1882,1888 ----
}

# Return the number of registered documents and list of files to index.
! return (scalar @{$rdocs{'name'}}, \@flist);
}

sub preupdate_registry(@) {
--
Shigekazu Aoyagi <aoyagi@xxxxx>

_______________________________________________
Namazu-devel-ja mailing list
Namazu-devel-ja@xxxxx
http://www.namazu.org/cgi-bin/mailman/listinfo/namazu-devel-ja


投稿者 xml-rpc : 2011年6月24日 17:00
役に立ちました?:
過去のフィードバック 平均:(0) 総合:(0) 投票回数:(0)
本記事へのTrackback: http://hoop.euqset.org/blog/mt-tb2006.cgi/104642
トラックバック
コメント
コメントする




画像の中に見える文字を入力してください。