Советы по чтению файла 50 ГБ (и перезаписи его в файлы 16 КБ)! - PullRequest
4 голосов
/ 03 мая 2011

У меня есть огромный файл (почти 50 ГБ, просто матрица в ASCII, состоящая из 360K строк, каждая с номерами 15K), и мне нужно его транспонировать. Чтобы не читать все это в памяти, я просто написал Perl-скрипт, который открывает 15К файлов (по одному на каждый столбец матрицы) и продолжает чтение полной строки входного файла и записывает каждое число в конец его соответствующий файл (первый номер для выходного файла column0.txt, второй номер для выходного файла column1.txt и т. д.).

Все выглядело многообещающе: код использует только 178 МБ постоянной памяти, и начальные тесты с только частью входного файла выполнялись идеально: он обработал 3600 строк примерно за минуту, поэтому я надеялся, что все это будет сделано в около двух часов, но когда я запускаю реальную вещь, код останавливается во многих точках. Например, в начале он действительно быстро обработал ~ 4600 строк, а затем остановился на некоторое время (возможно, 5-10 минут), прежде чем продолжить. Прямо сейчас, после ~ 10 часов вычислений, он обработал 131K строк, и код останавливается на две-три минуты после обработки 300-400 строк.

Я никогда не работал с такими большими входными файлами или с таким большим количеством открытых файлов, поэтому я не уверен, связана ли проблема с вводом или количеством дескрипторов файлов. Любой совет о том, как диагностировать (и, мы надеемся) решить проблему скорости? Я включаю соответствующую часть программы ниже

Спасибо

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

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 ]

1 голос
/ 03 мая 2011

Проверьте / proc / sys / fs / file-max, чтобы увидеть максимальное количество открытых файлов.
Вам может потребоваться прочитать файлы с помощью поиска, чтобы вы могли контролировать количество открытых файлов для чтения соответственно.
Лучше всего будет сделать кэш х строк, а затем добавить все файлы.

1 голос
/ 03 мая 2011

Некоторые мысли

1.Неявное разделение на @ _

$cols = split;

Выдает предупреждение:

Use of implicit split to @_ is deprecated

Если вы этого еще не сделали, вам следует добавить

use warnings;
use strict;

к вашему сценарию.(И учтите эти предупреждения.)

Попробуйте изменить $cols на @cols и вместо этого использовать $#cols в цикле for.Например,

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

2.Никакого chomp не требуется?

С split() in perlfunc:

Если также не указан PATTERN, разделяется на пробел (после пропуска любого начального пробела).

Это означает, что ваш символ новой строки также должен быть удален, поскольку он считается пробелом.

Следовательно, chomp() не требуется.

3.Количество открытых файлов

Я считаю, что Perl open() довольно быстрый, поэтому, возможно, стоит кэшировать ваши данные, как предложил Вайсмат.Пока вы это делаете, вы можете также использовать один дескриптор файла для всех файлов и открывать их только во время печати кэша.Например:

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

ETA: @column здесь содержит столбцы, транспонированные из DATA.Вместо печати используйте:

$column[$col] .= $cols[$col] . " ";
0 голосов
/ 03 мая 2011

Учитывая, что в результате получаются странные вещи, хорошей идеей может быть проверка успешности ваших отпечатков:

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: $! ";
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...