web-dev-qa-db-fra.com

Comment trier les gros fichiers?

J'ai un PC avec processeur Intel (R) Pentium (R) G640 @ 2,80 GHz et 8 Go de RAM. J'utilise Scientific Linux 6.5 avec le système de fichiers EXT3.

Sur cette configuration, quel est le moyen le plus rapide de faire un sort -u sur un fichier de 200 gigaoctets?

Dois-je diviser le fichier en fichiers plus petits (inférieurs à 8 Go), sort -u les, les assembler, puis les diviser à nouveau dans une taille différente, sort -u encore, etc.? Ou existe-t-il des scripts de tri, des programmes qui pourraient gérer des fichiers aussi gros avec ma quantité limitée de RAM?

37
evachristine

GNU sort (qui est la valeur par défaut sur la plupart des systèmes Linux), a un --parallel option. De http://www.gnu.org/software/coreutils/manual/html_node/sort-invocation.html :

"--Parallel = n"

Définissez le nombre de tris exécutés en parallèle sur n. Par défaut, n est défini sur le nombre de processeurs disponibles, mais limité à 8, car les gains de performances diminuent par la suite. Notez également que l'utilisation de n threads augmente l'utilisation de la mémoire par un facteur de log n. Voir également l'invocation nproc.

Puisque votre processeur a 2 cœurs, vous pouvez faire:

sort --parallel=2 -uo list-sorted.txt list.txt

Il est préférable de spécifier le nombre réel de cœurs car il peut y en avoir plus car le processeur a hyper-threading .

Vous pouvez également expérimenter avec Nice pour influencer la priorité de planification du processeur et ionice pour influencer la planification des E/S. Vous pouvez augmenter la priorité par rapport à d'autres processus comme celui-ci, je ne pense pas que cela vous donnera de grandes économies car ils sont généralement meilleurs pour vous assurer qu'un processus d'arrière-plan n'utilise pas trop trop ressources. Néanmoins, vous pouvez les combiner avec quelque chose comme:

Nice -n -20 ionice -c2 -n7 sort --parallel=2 -uo list-sorted.txt list.txt

Notez également que Gilles commenté, l'utilisation d'une seule commande de tri GNU sera plus rapide que toute autre méthode de décomposition du tri car l'algorithme est déjà optimisé pour gérer les grands Tout autre chose ralentira probablement les choses.

49
Graeme

L'utilisation de la commande sort sera probablement l'option la plus rapide.

Mais vous voudrez probablement fixer les paramètres régionaux à C.

sort -u ne rapporte pas de lignes uniques, mais une de chaque ensemble de lignes qui les trient de la même manière. Dans l'environnement local C, 2 lignes différentes ne trient pas nécessairement la même chose, mais ce n'est pas le cas dans la plupart des environnements locaux basés sur UTF-8 sur les systèmes GNU.

De plus, l'utilisation des paramètres régionaux C évite les frais généraux liés à l'analyse UTF-8 et au traitement des ordres de tri complexes, ce qui améliorerait considérablement les performances.

Donc:

LC_ALL=C sort -u file

Vous pouvez également améliorer les performances en utilisant un lecteur plus rapide (ou un lecteur différent de celui où se trouvent les fichiers d'entrée et/ou de sortie) pour les fichiers temporaires (en utilisant -T ou $TMPDIR variable d'environnement), ou en jouant avec -S option prise en charge par certaines implémentations sort).

Pour un certain type d'entrée ou pour un stockage lent, utilisez le --compress-program l'option de GNU sort (par exemple avec lzop) pourrait améliorer les performances en plus de l'utilisation du stockage.


Maintenant, juste une note pour ceux qui objectent (à juste titre dans une certaine mesure) que ce ne sera pas le bon ordre:

Je conviens qu'en tant qu'humain, j'aimerais voir Stéphane trier entre Stefan et Stephanie, mais:

  • Un ordinateur voudrait Stéphane trier après depuis é (au moins lorsqu'il est exprimé en U + 00E9) en tant que caractère ou les octets de son codage UTF-8 sont triés après (en termes de point de code ou de valeur d'octet). C'est un ordre de tri qui est très simple à implémenter et est un ordre total strict et n'a aucune surprise.
  • L'ordre de tri de vos paramètres régionaux ne sera probablement pas satisfaisant dans de nombreux cas, même pour un humain. Par exemple sur mon système avec les paramètres régionaux par_GB.utf8 par défaut:

    • Stéphane et Stéphane (l'un avec U + 00E9, l'autre avec eU + 0301) ne fait pas le même tri:

      $ printf '%b\n' 'Ste\u0301phane' 'St\u00e9phane' | sort -u
      Stéphane
      Stéphane
      
    • mais ③, ①, ② trient tous de la même manière (évidemment un bug dans ces définitions de paramètres régionaux¹):

      $ printf '%s\n' ③ ① ② | sort -u
      ③
      

      Ici, c'est ③, mais ça aurait tout aussi bien pu être ① ou ②

Donc, OMI, il y a de fortes chances que vous vouliez toujours sort -u avec LC_ALL = C, si vous voulez des lignes uniques. Et si vous souhaitez que la liste résultante soit triée dans l'ordre de tri de l'utilisateur, redirigez-la vers sort:

LC_ALL=C sort -u | sort

LC_ALL=C sort | LC_ALL=C uniq -c | sort -k2

¹ Édition 2019 . l'ordre de ① ② ③ ④ ⑤ ... a depuis été corrigé dans les nouvelles versions de la lib GNU libc, mais à partir de 2,30, plus de 95% des caractères n'ont toujours pas d'ordre défini) , vous pouvez remplacer ① ② ③ ④ ⑤ par ???? ???? ???? ???? ???? par exemple. Avec un peu de chance, GNU locales finira par être corrigé complètement ( ils devront le faire s'ils veulent se conformer à la prochaine révision de la norme ) et le problème sera alors limité aux paramètres régionaux définis par l'utilisateur

47
Stéphane Chazelas