4 votes

Conseils pour lire un fichier de 50 Go (et le réécrire en fichiers de 16 Ko) !

J'ai un énorme fichier (près de 50GB, juste une matrice en ASCII composée de 360K lignes, chacune avec 15K nombres), et j'ai besoin de le transposer. Pour éviter de tout lire en mémoire, j'ai écrit un script Perl script qui ouvre 15K fichiers (un pour chaque colonne de la matrice) et procède en lisant une ligne complète du fichier d'entrée, et en écrivant chaque nombre à la fin de son fichier correspondant (le premier nombre dans le fichier de sortie column0.txt, le deuxième dans le fichier de sortie column1.txt, etc.)

Les choses semblaient prometteuses : le code n'utilise que 178MB de RAM et les tests initiaux avec une partie seulement du fichier d'entrée se déroulent parfaitement : il traite 3600 lignes en une minute environ, et j'espérais donc terminer le tout en deux heures environ, mais lorsque j'exécute le vrai programme, le code s'arrête à de nombreux endroits. Par exemple, au début, il a traité ~4600 lignes très rapidement, puis s'est arrêté pendant un certain temps (peut-être 5-10 minutes) avant de continuer. En ce moment, après ~10 heures de calcul, il a traité 131K lignes et le code s'arrête pendant deux-trois minutes après avoir traité 300-400 lignes.

Je n'ai jamais travaillé avec des fichiers d'entrée aussi volumineux ni avec autant de fichiers ouverts, et je ne sais donc pas si le problème vient de l'entrée ou du nombre de descripteurs de fichiers. Des conseils sur la manière de diagnostiquer (et, je l'espère, de résoudre) le problème de vitesse ? J'ai inclus la partie pertinente du programme ci-dessous

Remerciements

\==================================

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 $!;

1voto

weismat Points 4354

Vérifiez /proc/sys/fs/file-max pour connaître le nombre maximal de fichiers ouverts.
Il se peut que vous deviez lire les fichiers en utilisant la fonction "seek" afin de pouvoir contrôler le nombre de fichiers ouverts pour la lecture.
La meilleure solution consiste à mettre en cache x lignes et à les ajouter ensuite à tous les fichiers.

1voto

TLP Points 48922

Quelques réflexions

1. Fractionnement implicite en @_

$cols = split;

Donne l'alerte :

Use of implicit split to @_ is deprecated

Si vous ne le faites pas déjà, vous devriez ajouter

use warnings;
use strict;

à votre script. (Et tenez compte de ces avertissements).

Envisager de modifier $cols à @cols au lieu d'utiliser $#cols dans la boucle for. Par exemple

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

2. Pas de chomp nécessaire ?

En split() en perlfunc :

Si PATTERN est également omis, les divisions sur (après avoir ignoré les d'espacement).

Cela signifie que le caractère de la nouvelle ligne doit également être supprimé, car il est considéré comme un caractère d'espacement.

C'est pourquoi, chomp() n'est pas nécessaire.

3. Nombre de fichiers ouverts

Je crois que la fonction open() est assez rapide, il peut donc être intéressant de mettre en cache vos données comme le suggère weismat. Pendant que vous faites cela, vous pourriez aussi partager un seul filehandle pour tous les fichiers, et ne les ouvrir que pendant l'impression du cache. Par exemple :

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

ETA : @column contient les colonnes transposées de DATA. Au lieu d'imprimer, utilisez :

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

0voto

ysth Points 54757

Étant donné que vous avez obtenu des résultats bizarres, il peut être judicieux de vérifier que vos impressions sont réussies :

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

Essayez d'effectuer un rinçage toutes les 500 lignes environ ? use IO::Handle; et après l'impression :

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

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X