Comment mesurer le temps d'exécution moyen d'un script?

23

J'ai deux scripts qui calculent chacun la factorielle d'un nombre. Je voudrais savoir lequel est le plus rapide. La timecommande me donne des millisecondes et le résultat est différent de temps en temps:

piousbox@piousbox-laptop:~/projects/trash$ time ruby fac2.rb
30414093201713378043612608166064768844377641568960512000000000000

real    0m0.089s
user    0m0.052s
sys 0m0.028s
piousbox@piousbox-laptop:~/projects/trash$ time ruby fac1.rb
30414093201713378043612608166064768844377641568960512000000000000

real    0m0.091s
user    0m0.048s
sys 0m0.036s
piousbox@piousbox-laptop:~/projects/trash$ time ruby fac1.rb
30414093201713378043612608166064768844377641568960512000000000000

real    0m0.088s
user    0m0.048s
sys 0m0.040s
piousbox@piousbox-laptop:~/projects/trash$ time ruby fac2.rb
30414093201713378043612608166064768844377641568960512000000000000

real    0m0.088s
user    0m0.048s
sys 0m0.028s
piousbox@piousbox-laptop:~/projects/trash$ time ruby fac1.rb
30414093201713378043612608166064768844377641568960512000000000000

real    0m0.087s
user    0m0.064s
sys 0m0.028s
piousbox@piousbox-laptop:~/projects/trash$ time ruby fac2.rb
30414093201713378043612608166064768844377641568960512000000000000

real    0m0.089s
user    0m0.068s
sys 0m0.016s
piousbox@piousbox-laptop:~/projects/trash$ 

Comment puis-je prendre le temps moyen nécessaire pour exécuter le script? Je pourrais analyser et faire la moyenne de la sortie d'un 100 time, mais j'imagine qu'il existe une meilleure solution?

Victor Piousbox
la source
1
stackoverflow.com/questions/8215482/…
Ciro Santilli 新疆 改造 中心 法轮功 六四 事件

Réponses:

4

Non, votre idée de la moyenne est correcte.

L'exécution du script dépend de nombreux facteurs, mais elle doit cependant être répartie entre le temps de configuration (chargement de l'interpréteur en mémoire, configuration et éventuellement compilation de code en bytecode ou code machine) et le vrai temps d'exécution.

Pour mieux vous concentrer sur le temps d'exécution interne, vous effectuez la boucle dans le script lui-même (c'est-à-dire qu'au lieu de calculer une factorielle, vous la calculez 100 fois au cours d'une même exécution du script. Le script sera configuré une fois et la routine interne exécutera 100 fois).

Pour vous concentrer sur le temps total, vous exécutez le script cent fois et faites la moyenne des résultats. Idéalement, vous devez séparer ces exécutions suffisamment pour que le système retourne à chaque fois dans un "état de référence" (ou un état sans rapport avec le script). Par exemple, l'interpréteur lui - même sera mis en cache en mémoire, de sorte que la toute première exécution du script sera sensiblement plus lente que les suivantes.

Pour avoir une meilleure idée de l'algorithme, je pense que la meilleure façon est quelque chose comme ça (sur une machine autrement inactive):

  • envelopper l'algorithme dans une seule fonction.
  • dans l'application de contrôle:
    • appeler la fonction une fois
    • obtenir l'heure du système ("horloge murale") et ajouter 10 (ou un N raisonnable) secondes
    • entrez dans la boucle et commencez à compter les itérations
    • après chaque appel à la fonction, incrémenter le compteur
    • si l'heure système est inférieure à l'heure enregistrée, faites une autre boucle
    • obtenir le N exact, éventuellement en virgule flottante, à partir de l'heure actuelle de l'horloge murale
    • afficher le compteur divisé par N: c'est le nombre d'itérations / seconde.

L'application ne s'exécute qu'une seule fois, toute la configuration et l'amorçage sont effectués par la première itération non chronométrée, donc cela devrait minimiser les frais généraux (sauf peut-être pour l'appel de temps).

Si la fonction reçoit une entrée, vous feriez bien de lui fournir une séquence aléatoire d'entrées à l'aide d'un PRNG prédéfini avec une valeur fixe, pour garantir que les deux versions de la fonction testée reçoivent les mêmes valeurs. On évite ainsi une fonction d' effectuer apparemment mieux en raison de « nombres chanceux » (par exemple , je me souviens d' une variation de l'algorithme Hillsort qui a effectué mieux si le mesurablement nombre d'éléments à trier était sous la forme 2 k -1 avec petit k s).

LSerni
la source
D'accord, merci. J'ai remarqué que les appels suivants se raccourcissaient. J'exécute maintenant la boucle dans les scripts et j'ai constaté qu'un algorithme est nettement plus rapide que l'autre.
Victor Piousbox
39

Vous pouvez exécuter des itérations du programme en boucle; et divisez le temps total par le nombre d'itérations:

time for i in {1..10}; do sleep 1; done
real    0m10.052s
user    0m0.005s
sys 0m0.018s
Kent
la source
2
Super simple, j'adore. Je n'ai également jamais vu {1..10}auparavant et je suis déconcerté que cela fonctionne, je ne le trouve pas dans le manuel bash. La seule chose triste est que vous ne connaissez pas la répartition de vos résultats (temps minimum et maximum).
w00t
@ w00t:man -P 'less +/Brace\ Expansion' bash
user2683246
Merci @ user2683246! Je l'ai ensuite également trouvé sur gnu.org/software/bash/manual/bash.html#Brace-Expansion - une belle utilisation de less btw. Maintenant, je suis également curieux de savoir quand cela est apparu dans bash…
w00t
1
Aha, version 3, 10 ans après avoir commencé à utiliser bash :) tldp.org/LDP/abs/html/bashver3.html
w00t
2
Si cela ne fonctionne pas pour les Googleurs qui arrivent, c'est peut-être parce que vous ne courez pas bash. Essayez de courir /bin/bashavant cela.
Cory Klein
14

il existe un outil appelé multitime qui fait exactement cela: exécuter une commande plusieurs fois, mesurer le temps qu'il faut (réel / utilisateur / système avec moyenne, min / max et temps médian calculés automatiquement)

Par exemple, pour mesurer 100 fois un script similaire:

multitime -q -n 100 "fact1.sh"
===> multitime results
1: -q fact1.sh
            Mean        Std.Dev.    Min         Median      Max
real        0.122       0.032       0.086       0.116       0.171       
user        0.148       0.044       0.096       0.137       0.223       
sys         0.023       0.019       0.000       0.014       0.061 
Cyril Chaboisseau
la source
12

Ceci est ancien mais il est apparu si haut sur Google lorsque je cherchais une commande que j'utilisais auparavant mais que je n'ai pas pu trouver. Quoi qu'il en soit, ma façon préférée de le faire est:

perf stat -r 10 -B sleep 1

Cela donne pas mal de détails, y compris le temps d'exécution moyen juste à la fin:

1.002248382 seconds time elapsed                   ( +-  0.01% )
Zaahid
la source
1

Hyperfine est une autre option.

Exemple d'utilisation:

hyperfine --warmup 3 'ruby fac1.rb'
Luís Bianchin
la source
1
hyperfine est tellement mieux que les autres alternatives que c'est ridicule. Détection des pistes nécessaires, échauffement, belle sortie, rapports de démarque, écrits en rouille, etc.
Klas Mellbourn