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?
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.
$ 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
[...]
$ 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
[...]
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.
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