执行Perl脚本时解决内存不足错误

时间:2011-11-14 21:59:21

标签: linux perl out-of-memory

我正在尝试基于英语维基百科转储中找到的前100K单词构建一个n-gram语言模型。我已经使用用Java编写的修改过的XML解析器提取出纯文本,但需要将其转换为vocab文件。

为了做到这一点,我找到了一个据说可以完成这项工作的perl脚本,但缺乏如何执行的指令。毋庸置疑,我是Perl的全新手,这是我第一次遇到使用它的需要。

当我运行这个脚本时,我在两个独立的双核机器上使用这个7.2GB文本文件时会出现内存不足错误,该机器有4GB内存和runnung Ubuntu 10.04和10.10。

当我联系作者时,他说这个脚本在带有4GB RAM的MacBook Pro上运行正常,在使用perl 5.12的6.6GB文本文件上执行时,内存总使用量约为78 MB。作者还说,该脚本逐行读取输入文件,并在内存中创建一个hashmap。

脚本是:

#! /usr/bin/perl

use FindBin;
use lib "$FindBin::Bin";

use strict;
require 'english-utils.pl';

## Create a list of words and their frequencies from an input corpus document
## (format: plain text, words separated by spaces, no sentence separators)

## TODO should words with hyphens be expanded? (e.g. three-dimensional)

my %dict;
my $min_len = 3;
my $min_freq = 1;

while (<>) {

    chomp($_);
    my @words = split(" ", $_);

    foreach my $word (@words) {

        # Check validity against regexp and acceptable use of apostrophe

        if ((length($word) >= $min_len) && ($word =~ /^[A-Z][A-Z\'-]+$/) 
        && (index($word,"'") < 0 || allow_apostrophe($word))) {
            $dict{$word}++;
        }
    }

}

# Output words which occur with the $min_freq or more often

foreach my $dictword (keys %dict) {
    if ( $dict{$dictword} >= $min_freq ) {
        print $dictword . "\t" . $dict{$dictword} . "\n";
    }
}

我正在通过mkvocab.pl corpus.txt

从命令行执行此脚本

附带的额外脚本只是一个正则表达式脚本,用于测试撇号的位置以及它们是否符合英语语法规则。

我认为内存泄漏是由于不同的版本,因为我的机器上安装了5.10。所以我升级到5.14,但错误仍然存​​在。根据{{​​1}},我的系统上有大约1.5GB的可用内存。

由于我完全不熟悉语言的语法和结构,您能否指出问题区域以及问题存在的原因以及如何解决问题。

2 个答案:

答案 0 :(得分:7)

如果单词中有一些重复,则可以将7,2Gb文件加载到哈希中,例如the发生了17,000次,等等。但这似乎相当多。

您的脚本假定文件中的行适当长。如果您的文件不包含换行符,您将整个文件加载到$_的内存中,然后用split加载内存加载,然后在哈希中添加更多内容。这会对任何系统造成压力。

一个想法可能是使用空格" "作为输入记录分隔符。它将大致完成你已经使用split进行的操作,除了它将单独留下其他空白字符,并且不会像过时那样修剪多余的空白。例如:

$/ = " ";
while (<>) {
    for my $word ( split ) {  # avoid e.g. "foo\nbar" being considered one word
        if (
              (length($word) >= $min_len) &&
              ($word =~ /^[A-Z][A-Z\'-]+$/) &&
              (index($word,"'") < 0 || allow_apostrophe($word))
        ) {
            $dict{$word}++;
        }
    }
}

这样就可以在一口大小的块中读取很长的行,假设你的单词之间有空格(而不是制表符或换行符)。

答案 1 :(得分:3)

尝试运行

dos2unix corpus.txt

您可能正在将整个文件作为一行阅读...