Hoe de uitvoeringstijd van een script effectief te krijgen?

Ik wil graag de voltooiingstijd van een script weergeven.

Wat ik momenteel doe is –

#!/bin/bash date ## echo the date at start # the script contents date ## echo the date at end 

Dit toont alleen de tijd van begin en einde van het script. Zou dat zo zijn? mogelijk om een fijnmazige uitvoer weer te geven, zoals processortijd / io-tijd, enz.?

Reacties

Antwoord

Jus t gebruik time wanneer je het script aanroept:

time yourscript.sh 

Reacties

  • Dit wordt drie keer uitgevoerd: real, user en sys. Zie hier voor de betekenis ervan.
  • en ” real ” is waarschijnlijk wat mensen willen weten – ” Echt is de tijd van de wandklok – tijd van begin tot einde van het gesprek ”
  • Is er een manier om de stdout in een bestand vast te leggen? Zoiets als time $( ... ) >> out.txt
  • U kunt dit ook doen met reeksen opdrachten op de opdrachtregel, bijvoorbeeld: time (command1 && command2)
  • Of hij kan, in zijn script, gewoon doen: echo $ SECONDS ervan uitgaande dat het bash is ….

Answer

Als time geen optie is,

start=`date +%s` stuff end=`date +%s` runtime=$((end-start)) 

Opmerkingen

  • Merk op dat dit alleen werkt als je ‘ geen precisie van minder dan een seconde nodig hebt. Voor sommige toepassingen kan dat acceptabel zijn , voor anderen niet. Voor een iets betere precisie (je ‘ roept nog steeds date twee keer aan, bijvoorbeeld, dus je kunt beste milliseconde precisie in de praktijk te krijgen, en waarschijnlijk minder), probeer dan date +%s.%N te gebruiken. (%N is nanoseconden sinds de hele seconde .)
  • @ChrisH Oh. Goed om erop te wijzen; bash rekenkundige uitbreiding is alleen integer. Ik zie twee obvi ous opties; laat de punt in de datumnotatiereeks achterwege (en behandel de resulterende waarde als nanoseconden sinds epoch), dus gebruik date +%s%N, of gebruik iets bekwamer zoals bc om de werkelijke looptijd te berekenen uit de twee waarden, zoals jwchew suggereert. Toch vind ik dat deze benadering een suboptimale manier is om het te doen; time is aanzienlijk beter indien beschikbaar, om de hierboven uiteengezette redenen.
  • Installeer gewoon bc en doe dit: runtime=$( echo "$end - $start" | bc -l )
  • Als uw script enkele minuten duurt, gebruik dan: echo "Duration: $((($(date +%s)-$start)/60)) minutes
  • Verder naar @ rubo77 opmerking, als uw script enkele uren duurt, gebruik dan hours=$((runtime / 3600)); minutes=$(( (runtime % 3600) / 60 )); seconds=$(( (runtime % 3600) % 60 )); echo "Runtime: $hours:$minutes:$seconds (hh:mm:ss)"

Answer

Bel gewoon times zonder argumenten bij het afsluiten van je script.

Met ksh of zsh, je kunt in plaats daarvan ook time gebruiken. Met zsh, time geeft je naast de gebruiker en ook de wandkloktijd systeem CPU-tijd.

Om de exit-status van uw script te behouden, kunt u ervoor zorgen dat:

ret=$?; times; exit "$ret" 

Of u kan ook een trap toevoegen op EXIT:

trap times EXIT 

Op die manier zullen tijden worden aangeroepen wanneer de shell wordt afgesloten en de exit-status blijft behouden.

$ bash -c "trap times EXIT; : {1..1000000}" 0m0.932s 0m0.028s 0m0.000s 0m0.000s $ zsh -c "trap time EXIT; : {1..1000000}" shell 0.67s user 0.01s system 100% cpu 0.677 total children 0.00s user 0.00s system 0% cpu 0.677 total 

Merk ook op dat alle bash, ksh en zsh hebben een $SECONDS speciale variabele die automatisch elke seconde wordt opgehoogd. In zowel zsh als ksh93 kan die variabele ook als zwevende komma worden gemaakt (met typeset -F SECONDS ) voor meer precisie. Dit is alleen wandkloktijd, geen CPU-tijd.

Opmerkingen

  • Die $ SECONDS-variabele is erg handig, bedankt!
  • Elimineert uw aanpak het effect van tijdelijke gebeurtenissen? – – Ik denk dat uw benadering in de buurt komt van de time -benadering die eerder is gepresenteerd.- – Ik denk dat de timeit benadering gepresenteerd in MATLAB-code hier nuttig kan zijn.
  • Hallo, @St é phane Chazelas. Ik heb ontdekt dat times ‘ geen tijd aan de muur geeft, toch? bijvoorbeeld bash -c 'trap times EXIT;sleep 2'
  • @Binarus, ja, die functie komt van ksh. In tegenstelling tot ksh93 / zsh, behalve dat bash geen drijvende-komma ondersteunt, ‘ is ook onderbroken doordat nadat je SECONDS op 0 hebt ingesteld, het later zal veranderen in 1 van 0 tot 1 seconde ( aangezien het alleen rekening houdt met het resultaat van time() dat alleen volledige tweede granulariteit heeft).
  • @Binarus, niet dat ‘ s tussen 0 en 1. Als u SECONDEN op 0 instelt om 12: 00: 00.000, verandert dit een seconde later in 1. Maar als u het instelt op 12: 00: 00.999, verandert het een milliseconde later in 1. Maar ja, ik ben het ermee eens dat ‘ in de praktijk waarschijnlijk niet veel uitmaakt als je niet ‘ toch een precisie van minder dan een seconde hebt.

Answer

Ik “ben een beetje laat op de trein, maar wilde mijn oplossing posten (voor precisie van minder dan een seconde) ) voor het geval anderen toevallig deze thread tegenkomen tijdens het zoeken. De uitvoer is in de indeling dagen, uren, minuten en ten slotte seconden:

res1=$(date +%s.%N) # do stuff in here res2=$(date +%s.%N) dt=$(echo "$res2 - $res1" | bc) dd=$(echo "$dt/86400" | bc) dt2=$(echo "$dt-86400*$dd" | bc) dh=$(echo "$dt2/3600" | bc) dt3=$(echo "$dt2-3600*$dh" | bc) dm=$(echo "$dt3/60" | bc) ds=$(echo "$dt3-60*$dm" | bc) LC_NUMERIC=C printf "Total runtime: %d:%02d:%02d:%02.4f\n" $dd $dh $dm $ds 

Hoop dat iemand daarbuiten vindt dit nuttig!

Reacties

  • Ik denk dat er geen andere (alleen bash) manier is dan ‘ bc ‘ om de berekeningen uit te voeren. Trouwens echt goed script;)
  • Pas op dat FreeBSD ‘ s date ondersteunt geen sub-seconde precisie en voegt gewoon de letterlijke “N” toe aan het tijdstempel.
  • Heel mooi script, maar mijn bash doet het niet ‘ t omgaan met het subseconde deel. Ik heb ook geleerd dat / 1 in bc dat effectief verwijdert, dus heb ik @ / 1 @ toegevoegd aan de $ ds-berekening, en het toont nu heel mooie dingen!
  • bc ondersteunt modulo: dt2=$(echo "$dt%86400" | bc). Gewoon zeggen … Trouwens, ik geef de voorkeur aan de vorm dt2=$(bc <<< "${dt}%86400") maar dat ‘ is geheel persoonlijk.
  • Ik niet ‘ weet nog niets van bc, maar de deling door 86400 maakt me wantrouwend voor de volgende reden: Er zijn dagen die geen 86400 seconden hebben, bijvoorbeeld dagen waarop de tijd wordt omgeschakeld van zomertijd naar normale tijd en vice versa, of dagen met schrikkelseconden. Als dergelijke dagen deel uitmaken van de tijdspanne die u wilt berekenen, dwz tussen res1 en res2 liggen (inclusief deze waarden), uw script zal waarschijnlijk mislukken.

Antwoord

Mijn methode voor bash :

# Reset BASH time counter SECONDS=0 # # do stuff # ELAPSED="Elapsed: $(($SECONDS / 3600))hrs $((($SECONDS / 60) % 60))min $(($SECONDS % 60))sec" 

Reacties

  • Dit toont de verstreken tijd, maar niet ‘ t toon ” fijnkorrelige uitvoer zoals processortijd, I / O-tijd ” zoals gevraagd in de vraag.
  • Wie op zoek is naar een antwoord op de gestelde vraag, zal deze oplossing waarschijnlijk nuttig vinden. Uw opmerking is beter gericht op de OP-vraag.

Antwoord

Persoonlijk wil ik al mijn scriptcode in een of andere “hoofd” -functie zoals:

main () { echo running ... } # stuff ... # calling the function at the very end of the script time main 

Merk op hoe gemakkelijk het is om het time commando te gebruiken in dit scenario. Het is duidelijk dat u “niet de exacte tijd meet, inclusief de tijd van de scriptontleding, maar ik vind het in de meeste situaties nauwkeurig genoeg.

Opmerkingen

  • Geweldige oplossing, heeft geen ‘ geen extra tools nodig en staat op zichzelf.

Antwoord

#!/bin/bash start=$(date +%s.%N) # HERE BE CODE end=$(date +%s.%N) runtime=$(python -c "print(${end} - ${start})") echo "Runtime was $runtime" 

Ja, dit roept Python aan, maar als je daarmee kunt leven, dan is dit best een aardige, beknopte oplossing.

Opmerkingen

  • Pas op dat het uitvoeren van dat python -commando in een subshell en het lezen van de uitvoer ervan enkele miljoenen nanoseconden zal duren op de meeste huidige systemen. Hetzelfde voor het uitvoeren van date.
  • ” Enkele miljoenen nanoseconden ” is enkele milliseconden . Mensen die bash-scripts timen, maken zich daar meestal niet veel zorgen over (tenzij ze miljarden scripts per megaseconde uitvoeren).
  • Merk ook op dat, zelfs als de berekening wordt gedaan in een python-proces, de waarden zijn volledig verzameld voordat python werd aangeroepen. De uitvoeringstijd van het script wordt correct gemeten, zonder de ” miljoenen nanoseconden ” overhead.
  • time main hierboven is zoveel schoner …ja, we kunnen leven met veel degradatie, maar het is ‘ veel beter zonder dus laten ‘ s ingenieurs blijven!
  • $(echo $end - $start | bc) zal hetzelfde doen zonder python

Answer

Deze vraag is vrij oud, maar toen ik probeerde mijn favoriete manier te vinden om het te doen, kwam deze thread hoog … en het verbaasde me dat niemand het noemde:

perf stat -r 10 -B sleep 1 

“perf” is een prestatieanalysetool opgenomen in de kernel onder “tools / perf” en vaak beschikbaar om te installeren als een apart pakket (“perf” in CentOS en “linux-tools” op Debian / Ubuntu). De Linux Kernal perf Wiki heeft veel meer informatie erover.

Het uitvoeren van “perf stat” geeft nogal wat details, inclusief de gemiddelde uitvoeringstijd helemaal aan het einde:

1.002248382 seconds time elapsed ( +- 0.01% ) 

Reacties

  • Wat is perf?
  • @B Layer – bewerkt mijn antwoord t o geef een korte beschrijving van ‘ perf ‘.
  • perf stat klaagt nu over ” Je hebt misschien geen toestemming om statistieken te verzamelen. ” tenzij je een aantal opdrachten uitvoert met sudo, wat het nutteloos maakt in alle scenarios waarin u ‘ t niet volledig uw doelmachine bezit.
  • Geweldig, bedankt! Veel fijnmazigere oplossing dan time anderen suggereren. In het bijzonder is time niet van toepassing op oplossingen voor het meten van codecompetities (aangezien het niet de ‘ afdruktijd in milliseconden en minder) , terwijl uw oplossing dat wel doet.

Answer

Een kleine shell-functie die eerder kan worden toegevoegd commandos om hun tijd te meten:

tm() { local start=$(date +%s) $@ local exit_code=$? echo >&2 "took ~$(($(date +%s)-${start})) seconds. exited with ${exit_code}" return $exit_code } 

Gebruik het dan in je script, of op je commandoregel zoals:

tm the_original_command with all its parameters 

Reacties

  • Het zou de moeite waard zijn om milliseconden toe te voegen, probeerde ilke s=$(date +%s000), niet zeker als het werkt.
  • @loretoparisi voor milliseconden, zou je date +%s%N kunnen proberen. Zie serverfault.com/questions/151109/…
  • Dit is geweldig, behalve veiliger gebruik "$@"

Answer

#!/bin/csh #PBS -q glean #PBS -l nodes=1:ppn=1 #PBS -l walltime=10:00:00 #PBS -o a.log #PBS -e a.err #PBS -V #PBS -M [email protected] #PBS -m abe #PBS -A k4zhang-group START=$(date +%s) for i in {1..1000000} do echo 1 done END=$(date +%s) DIFF=$(echo "$END - $START" | bc) echo "It takes DIFF=$DIFF seconds to complete this task..." 

Reacties

  • Hoe verschilt dit echt van de andere reeds gegeven antwoorden die date voor en na gebruiken het script en voer het verschil tussen hen uit?

Antwoord

  1. Gewoon gebruik time [any command] . Vb: time sleep 1 slaapt gedurende een realtime (dwz: zoals getimed door een stopwatch) van ~ 1.000 tot ~ 1.020 sec, zoals hier getoond:

      $ time sleep 1 real 0m1.011s user 0m0.004s sys 0m0.000s  

    Wat een mooi ding. Je kunt er elk commando achter zetten, en het geeft het resultaat weer in een mooie, voor mensen leesbare vorm. Ik gebruik het echt graag voor timing-builds. Bijv .:

      # time your "make" build time make # time your "Bazel" build time bazel build //path/to/some:target  

    … of voor git-bewerkingen die mogelijk echt kunnen zijn lang, zodat ik realistische mentale verwachtingen kan ontwikkelen:

      # time how long it takes to pull from a massive repo when # I"m working from home during COVID-19. NB: `git pull` # is sooooo much slower than just pulling the one branch # you need with `git pull origin <branch>`, so just fetch # or pull what you need! time git pull origin master  
  2. Voor meer aangepaste timingbehoeften waar u mogelijk de uitvoer moet manipuleren of naar andere vormen moet converteren, in bash , gebruik de interne $SECONDS variabele. Hier “een demo, inclusief het converteren van deze seconden naar andere eenheden, zoals minuten met drijvende komma:

    Let op: dt_min wordt afgerond van 0.01666666666... (1 seconde = zoveel minuten) naar 0.017 in dit geval omdat ik de functie printf gebruik om af te ronden. Het sleep 1; gedeelte hieronder is waar je “je script zou oproepen om uit te voeren en de tijd te nemen, maar ik” slaap gewoon voor 1 seconde omwille van deze demo.

    Commando:

      start=$SECONDS; sleep 1; end=$SECONDS; dt_sec=$(( end - start )); \ dt_min=$(printf %.3f $(echo "$dt_sec/60" | bc -l)); \ echo "dt_sec = $dt_sec; dt_min = $dt_min"  

    Uitvoer:

      dt_sec = 1; dt_min = 0.017  

Gerelateerd:

  1. Lees meer ongeveer bc en printf in mijn antwoord hier: https://stackoverflow.com/questions/12722095/how-do-i-use-floating-point-division-in-bash/58479867#58479867
  2. Ik weet niet meer waar ik voor het eerst hoorde over het time -commando, maar het kan afkomstig zijn van @Trudbert “s antwoord hier .

Antwoord

Gebruik alleen bash het is ook mogelijk om te meten en te berekenen de tijdsduur voor een deel van het shellscript (of de verstreken tijd voor het hele script):

start=$SECONDS ... # do time consuming stuff end=$SECONDS 

u kunt nu ofwel gewoon het verschil afdrukken:

echo "duration: $((end-start)) seconds." 

als je alleen een oplopende duur nodig hebt, doe dan:

echo "duration: $((SECONDS-start)) seconds elapsed.." 

Je kunt de duur ook opslaan in een variabele:

let diff=end-start 

Reacties

  • ^ DIT is het antwoord mensen. Of eenvoudiger: $ SECONDS aan het einde. Het ‘ is een INGEBOUWDE Bash-variabele. Alle andere antwoorden doen gewoon extra werk om dit wiel opnieuw uit te vinden …
  • Dit is slechts een herhaling van het eerdere antwoord van Marks unix.stackexchange .com / a / 340156/155362 .

Antwoord

Timingfunctie gebaseerd op SECONDS, beperkt tot alleen granulariteit op het tweede niveau, “gebruikt geen externe opdrachten:

time_it() { local start=$SECONDS ts ec printf -v ts "%(%Y-%m-%d_%H:%M:%S)T" -1 printf "%s\n" "$ts Starting $*" "$@"; ec=$? printf -v ts "%(%Y-%m-%d_%H:%M:%S)T" -1 printf "%s\n" "$ts Finished $*; elapsed = $((SECONDS-start)) seconds" # make sure to return the exit code of the command so that the caller can use it return "$ec" } 

Bijvoorbeeld:

time_it sleep 5 

geeft

2019-03-30_17:24:37 Starting sleep 5 2019-03-30_17:24:42 Finished sleep 5; elapsed = 5 seconds 

Antwoord

Ingebouwde bash-tijd gebruiken?

time: time [-p] PIPELINE Execute PIPELINE and print a summary of the real time, user CPU time, and system CPU time spent executing PIPELINE when it terminates. The return status is the return status of PIPELINE. The `-p" option prints the timing summary in a slightly different format. This uses the value of the TIMEFORMAT variable as the output format. 

Voorbeeld:

TIMEFORMAT="The command took %Rs" time { sleep 0.1 } 

Uitvoer:

The command took 0.108s 

Antwoord

Hier is een variant van Alex “s antwoord. Ik geef alleen om minuten en seconden, maar ik wilde het ook anders opmaken. Dus ik deed dit:

start=$(date +%s) end=$(date +%s) runtime=$(python -c "print "%u:%02u" % ((${end} - ${start})/60, (${end} - ${start})%60)") 

Reacties

  • Waarom downvote? Heb ik een bug?
  • Het gebruik van python voor die berekening lijkt mij ook een bug, sorry man. Waarom mensen ‘ niet time kunnen bellen, is mij een raadsel.

Antwoord

#!/bin/bash begin=$(date +"%s") Script termin=$(date +"%s") difftimelps=$(($termin-$begin)) echo "$(($difftimelps / 60)) minutes and $(($difftimelps % 60)) seconds elapsed for Script Execution." 

Antwoord

De geaccepteerde oplossing met time schrijft naar stderr.
De oplossing met times schrijft naar stdout.
De oplossingen die $SECONDS gebruiken, missen precisie van minder dan een seconde.
De andere oplossingen omvatten het aanroepen van externe programmas zoals date of perf wat niet efficiënt is.
Als je het goed vindt met een van deze, gebruik het dan.

Maar als u een efficiënte oplossing nodig heeft om de tijden met milliseconde precisie en ze nodig hebben in variabelen zodat de originele uitvoer blijft ongestoord u kunt procesvervanging combineren met enkele omleidingen rond time, wat veel sneller is dan bellen externe programmas en staat omleidingen rond het timing wrapper-script toe zoals bij het originele commando / script.

# Preparations: Cmd=vgs # example of a program to be timed which is writing to stdout and stderr Cmd="eval { echo stdout; echo stderr >&2; sleep 0.1; }" # other example; replace with your own TIMEFORMAT="%3R %3U %3S" # make time output easy to parse 

Selecteer een van de volgende varianten door de uitvoer van time passend bij uw behoeften:
Kortste variant waarbij stdout van $Cmd wordt geschreven naar stderr en niets aan stdout:

read Elapsed User System < <({ time $Cmd 2>&3; } 3>&2 2>&1 >&3) 

Langere variant die blijft origineel stdout en stderr scheiden van elkaar:

{ read Elapsed User System < <({ time $Cmd 2>&4; } 4>&2 2>&1 >&3); } 3>&1 

De meest gecompliceerde variant omvat het sluiten van de extra bestandsbeschrijvingen zodat $Cmd wordt aangeroepen alsof er geen timing-wrapper eromheen is en lvm commandos zoals vgs klagen niet over gelekte bestandsbeschrijvingen:

{ read Elapsed User System < <({ time $Cmd 2>&4 4>&-; } 4>&2 2>&1 >&3 3>&-); } 3>&1 

U kan zelfs een drijvende-komma-toevoeging in bash vervalsen zonder bc aan te roepen, wat veel langzamer zou zijn:

CPU=`printf %04d $((10#${User/.}+10#${System/.}))` # replace with your own postprocessing echo CPU ${CPU::-3}.${CPU: -3} s, Elapsed $Elapsed s >&2 # redirected independent of $Cmd 

Mogelijke uitvoer met de twee voorbeelden van $Cmd op een langzame CPU:

File descriptor 3 (/dev/pts/1) leaked on vgs invocation. Parent PID 10756: bash File descriptor 4 (/dev/pts/1) leaked on vgs invocation. Parent PID 10756: bash VG #PV #LV #SN Attr VSize VFree b3 3 24 0 wz--n- 1.31t 1.19t CPU 0.052 s, Elapsed 0.056 s 

Of:

stdout stderr CPU 0.008 s, Elapsed 0.109 s 

Geef een reactie

Het e-mailadres wordt niet gepubliceerd. Vereiste velden zijn gemarkeerd met *