web-dev-qa-db-fra.com

Pourquoi "écho" est-il tellement plus rapide que "toucher"?

J'essaie de mettre à jour l'horodatage à l'heure actuelle sur tous les fichiers xml de mon répertoire (récursivement). J'utilise Mac OSX 10.8.5.

Sur environ 300 000 fichiers, la commande echo suivante prend 10 secondes:

for file in `find . -name "*.xml"`; do echo >> $file; done

Cependant, la commande touch suivante prend 10 minutes! :

for file in `find . -name "*.xml"`; do touch $file; done

Pourquoi l'écho est-il tellement plus rapide que toucher ici?

116
Casey Patton

En bash, touch est un binaire externe, mais echo est un shell intégré :

$ type echo
echo is a Shell builtin
$ type touch
touch is /usr/bin/touch

Étant donné que touch est un binaire externe et que vous appelez touch une fois par fichier, le shell doit créer 300 000 instances de touch, ce qui prend beaucoup de temps.

echo, cependant, est une commande intégrée de Shell, et l'exécution des commandes intégrées de Shell ne nécessite pas du tout de bifurcation. Au lieu de cela, le shell actuel effectue toutes les opérations et aucun processus externe n'est créé; c'est la raison pour laquelle c'est tellement plus rapide.

Voici deux profils des opérations de Shell. Vous pouvez voir que beaucoup de temps est consacré au clonage de nouveaux processus lors de l'utilisation de touch. En utilisant /bin/echo au lieu du shell intégré devrait afficher un résultat beaucoup plus comparable.


Utiliser le toucher

$ strace -c -- bash -c 'for file in a{1..10000}; do touch "$file"; done'
% time     seconds  usecs/call     calls    errors syscall
------ ----------- ----------- --------- --------- ----------------
 56.20    0.030925           2     20000     10000 wait4
 38.12    0.020972           2     10000           clone
  4.67    0.002569           0     80006           rt_sigprocmask
  0.71    0.000388           0     20008           rt_sigaction
  0.27    0.000150           0     10000           rt_sigreturn
[...]

Utiliser l'écho

$ strace -c -- bash -c 'for file in b{1..10000}; do echo >> "$file"; done'
% time     seconds  usecs/call     calls    errors syscall
------ ----------- ----------- --------- --------- ----------------
 34.32    0.000685           0     50000           fcntl
 22.14    0.000442           0     10000           write
 19.59    0.000391           0     10011           open
 14.58    0.000291           0     20000           dup2
  8.37    0.000167           0     20013           close
[...]
160
Chris Down

Comme d'autres l'ont répondu, l'utilisation de echo sera plus rapide que touch car echo est une commande qui est généralement (mais pas obligatoirement) intégrée au shell. Son utilisation évite la surcharge du noyau associée à l'exécution d'un nouveau processus pour chaque fichier que vous obtenez avec touch.

Cependant, notez que le moyen le plus rapide pour obtenir cet effet est toujours d'utiliser touch, mais plutôt que d'exécuter le programme une fois pour chaque fichier, il est possible d'utiliser le -exec option avec find pour garantir qu'elle n'est exécutée que quelques fois. Cette approche sera généralement plus rapide car elle évite la surcharge associée à une boucle Shell:

find . -name "*.xml" -exec touch {} +

En utilisant le + (par opposition à \;) avec find ... -exec n'exécute la commande qu'une seule fois si possible avec chaque fichier comme argument. Si la liste d'arguments est très longue (comme c'est le cas avec 300 000 fichiers), plusieurs exécutions seront effectuées avec une liste d'arguments dont la longueur est proche de la limite (ARG_MAX sur la plupart des systèmes).

Un autre avantage de cette approche est qu'elle se comporte de manière robuste avec des noms de fichiers contenant tous les espaces, ce qui n'est pas le cas avec la boucle d'origine.

71
Graeme

echo est une fonction intégrée de Shell. D'un autre côté, touch est un binaire externe.

$ type echo
echo is a Shell builtin
$ type touch
touch is hashed (/usr/bin/touch)

Shell intégré sont beaucoup plus rapides car il n'y a pas de surcharge impliquée dans le chargement du programme, c'est-à-dire qu'il n'y a pas de fork/exec impliqué. En tant que tel, vous observeriez un décalage horaire important lors de l'exécution d'un grand nombre de commandes externes par rapport à une commande externe.

C'est la raison pour laquelle des utilitaires comme time sont disponibles en tant que commandes internes de Shell.

Vous pouvez obtenir la liste complète des fonctions intégrées de Shell en disant:

enable -p

Comme mentionné ci-dessus, l'utilisation de l'utilitaire par opposition à l'utilitaire intégré entraîne une dégradation significative des performances. Voici les statistiques du temps nécessaire pour créer ~ 9000 fichiers en utilisant le intégré echo et le utilitaire echo:

# Using builtin
$ time bash -c 'for i in {1000..9999}; do echo > $i; done'

real    0m0.283s
user    0m0.100s
sys 0m0.184s

# Using utility /bin/echo
$ time bash -c 'for i in {1000..9999}; do /bin/echo > $i; done'

real    0m8.683s
user    0m0.360s
sys 0m1.428s
29
devnull