有关读取50GB文件(并将其重写为16K文件)的建议!

时间:2011-05-03 08:56:52

标签: perl

我有一个巨大的文件(接近50GB,只是一个由360K行组成的ASCII矩阵,每行有15K个数字),我需要转置它。为了避免在内存中读取整个内容,我只编写了一个打开15K文件的Perl脚本(矩阵的每一列一个),然后读取输入文件的完整行,并将每个数字写入其结尾相应的文件(输出文件column0.txt的第一个数字,输出文件column1.txt的第二个数字等)。

事情看起来很有希望:代码只使用一个178MB的常量RAM,并且只有部分输入文件的初始测试运行得很好:它在大约一分钟内处理了3600行,所以我希望能完成整个过程大约两个小时,但是当我运行真实的东西时,代码会在许多点停止。例如,在开始时它很快就处理了~4600行,然后停止了很长一段时间(可能是5-10分钟),然后再继续。现在,经过大约10个小时的计算,它处理了131K行,处理300-400行后代码停止了2-3分钟。

我从未使用过这么大的输入文件或打开过多的文件,所以我不确定问题是输入还是文件描述符的数量。关于如何诊断(并希望)解决速度问题的任何建议?我在下面列出了该计划的相关部分

由于

==================================

for ($i=0 ; $i<$columnas ; $i++) {
    $column[$i]  = IO::File->new(">column$i.txt") or die $!;
}

while (<DATA>) {
    chomp;
    $cols = split;

    for ($col=0 ; $col<$cols ; $col++) {
        print { $column[$col] } "$_[$col] " ;
    }
}

close (DATA) or die $!;

3 个答案:

答案 0 :(得分:1)

检查/ proc / sys / fs / file-max以查看最大打开文件数 您可能需要使用搜索来阅读文件,以便您可以相应地控制打开文件的数量 最好的做法是缓存x行,然后附加到所有文件。

答案 1 :(得分:1)

一些想法

<强> 1。隐式拆分为@_

$cols = split;

发出警告:

Use of implicit split to @_ is deprecated

如果你还没有这样做,你应该添加

use warnings;
use strict;

到您的脚本。 (注意那些警告。)

考虑将$cols更改为@cols,而在for循环中使用$#cols。 E.g。

@cols = split;
for (my $col=0; $col <= $#cols; $col++)

<强> 2。不需要chomp?

来自split()中的perlfunc

  

如果也省略了PATTERN,则拆分   空白(跳过任何领先之后)   空白)。

这意味着您的换行符也应该被删除,因为它被视为空格字符。

因此,chomp()不是必需的。

第3。打开文件数

我相信perl的open()相当快,所以像weismat建议的那样,可能值得缓存你的数据。在执行此操作时,您可以共享所有文件的单个文件句柄,并仅在打印缓存时打开它们。 E.g:

for ($i = 0; $i <= $#column; $i++) {
    open OUT, ">> column$i.txt" or die $!;
    print OUT $column[$i];
}

ETA:@column此处包含从DATA转置的列。而不是打印,使用:

$column[$col] .= $cols[$col] . " ";

答案 2 :(得分:0)

鉴于你得到了奇怪的东西,检查你的打印成功可能是一个好主意:

print { $column[$col] } "$_[$col] "
    or die "Error printing column $col: $! ";

尝试冲洗每500行左右? use IO::Handle;并在印刷之后:

if ( $. % 500 == 0 ) {
    $column[$col]->flush()
        or die "Flush of column $col failed: $! ";
}