Voici le problème auquel je suis confronté:
Le temps de lecture/écriture du fichier lui-même prend des heures, je voudrais donc trouver un moyen d'améliorer ce qui suit:
cat file1 file2 file3 ... fileN >> newBigFile
Cela nécessite le double de l'espace disque comme file1
... fileN
prend 100 Go, puis newBigFile
prend encore 100 Go, puis file1
... fileN
est supprimé.
Les données sont déjà dans file1
... fileN
, faisant le cat >>
entraîne un temps de lecture et d'écriture alors que tout ce dont j'ai vraiment besoin, c'est que les centaines de fichiers réapparaissent en un seul fichier ...
Si vous n'avez pas besoin d'un accès aléatoire au gros fichier final (c'est-à-dire que vous ne le lisez qu'une seule fois du début à la fin), vous pouvez faire apparaître vos centaines de fichiers intermédiaires comme un seul. Où vous feriez normalement
$ consume big-file.txt
au lieu de cela
$ consume <(cat file1 file2 ... fileN)
Cela utilise Unix substitution de processus , parfois aussi appelé "canaux nommés anonymes".
Vous pouvez également économiser du temps et de l'espace en divisant votre entrée et en effectuant le traitement en même temps; GNU Parallel a un - pipe switch qui fera exactement cela. Il peut également réassembler les sorties dans un seul gros fichier, utilisant potentiellement moins d'espace de travail car il n'a besoin que de conserver nombre de cœurs sur le disque à une fois que. Si vous exécutez littéralement vos centaines de processus en même temps, Parallel améliorera considérablement votre efficacité en vous permettant de régler la quantité de parallélisme avec votre machine. Je le recommande fortement.
Peut-être que dd
serait plus rapide car vous n'auriez pas à passer de trucs entre cat et Shell. Quelque chose comme:
mv file1 newBigFile
dd if=file2 of=newBigFile seek=$(stat -c %s newBigFile)
Lors de la concaténation de fichiers, vous pouvez supprimer les petits fichiers à mesure qu'ils sont ajoutés:
for file in file1 file2 file3 ... fileN; do
cat "$file" >> bigFile && rm "$file"
done
Cela éviterait d'avoir besoin de doubler l'espace.
Il n'y a pas d'autre moyen de créer des fichiers comme par magie. L'API du système de fichiers n'a tout simplement pas de fonction qui le fait.
Est-il possible que vous ne divisiez simplement pas le fichier? Au lieu de cela, traitez le fichier en morceaux en définissant le pointeur de fichier dans chacun de vos travailleurs parallèles. Si le fichier doit être traité de manière orientée ligne, cela le rend plus délicat mais cela peut toujours être fait. Chaque travailleur doit comprendre que plutôt que de commencer à l'offset que vous lui donnez, il doit d'abord rechercher octet par octet le prochain saut de ligne +1. Chaque travailleur doit également comprendre qu'il ne traite pas la quantité définie d'octets que vous lui donnez mais doit traiter la première nouvelle ligne après la quantité définie d'octets qu'il est alloué au traitement.
L'allocation et le réglage réels du pointeur de fichier sont assez simples. S'il y a n travailleurs, chacun traite n/octets de taille de fichier et le pointeur de fichier commence au numéro de travailleur * n/taille_fichier.
y a-t-il une raison pour laquelle ce type de plan n'est pas suffisant?
Solution rapide mais pas gratuite? Obtenez un disque SSD ou un stockage flash PCIe. Si c'est quelque chose qui doit être fait régulièrement, l'augmentation de la vitesse du disque IO va être l'accélération la plus rentable et la plus rapide que vous puissiez obtenir.
Je pense que c'est le moyen le plus rapide de récupérer tous les fichiers contenus dans le même dossier:
$ ls [path to folder] | while read p; do cat $p; done
tout ce dont j'ai vraiment besoin, c'est que les centaines de fichiers réapparaissent comme 1 fichier ...
La raison pour laquelle il n'est pas pratique de simplement joindre des fichiers de cette façon au niveau du système de fichiers car les fichiers texte ne remplissent généralement pas exactement un bloc de disque, de sorte que les données des fichiers suivants devraient être remontées pour combler les lacunes, provoquant une tas de lectures/écritures de toute façon .
Il existe une trop grande concurrence.
Une meilleure façon de procéder serait d'utiliser des lectures à accès aléatoire dans le fichier sur les plages souhaitées et de ne jamais le diviser et traiter uniquement le nombre de fichiers en tant que nombre de CPU/cœurs physiques dans la machine. C'est à moins que cela n'inonde également le disque avec IOPS, alors vous devez réduire jusqu'à ce que le disque ne soit pas le goulot d'étranglement.
Ce que vous faites de toute façon avec tout le fractionnement/copie/suppression naïf génère des tonnes d'IOPS et il n'y a aucun moyen de contourner la physique de celui-ci.
Une solution transparente qui représenterait probablement plus de travail que n'en vaut la peine, à moins qu'il ne s'agisse d'un problème/problème quotidien, consiste à écrire un système de fichiers Fuse personnalisé qui représente un seul fichier en tant que plusieurs fichiers. Il existe de nombreux exemples sur la façon de traiter le contenu des fichiers d'archive sous forme de fichiers individuels qui vous montreront les bases de la procédure à suivre.