Hvordan får man udført tid på et script effektivt?

Jeg vil gerne vise afslutningstiden for et script.

Hvad jeg i øjeblikket gør er –

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

Dette viser bare tidspunktet for start og slutning af scriptet. Ville det være muligt at vise et fint kornet output som processortid / io tid osv.?

Kommentarer

Svar

Jus Brug ikke time når du kalder scriptet:

time yourscript.sh 

Kommentarer

  • Dette output tre gange: real, user og sys. For betydningerne af disse, se her .
  • og ” real ” er sandsynligvis, hvad folk vil vide – ” Real er tid til vægur – tid fra start til slut af opkaldet ”
  • Er der en måde at fange stdout i en fil? For eksempel noget som time $( ... ) >> out.txt
  • Du kan også gøre dette til sekvenser af kommandoer på kommandolinjen, f.eks: time (command1 && command2)
  • Eller han kunne i sit script bare gøre: ekko $ SECONDS forudsat at det er bash ….

Svar

Hvis time ikke er en mulighed,

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

Kommentarer

  • Bemærk, at dette kun virker, hvis du ikke ‘ ikke har brug for præcision i sekund. For nogle anvendelser kan det være acceptabelt , for andre ikke. For lidt bedre præcision (du ‘ påkalder stadig date for eksempel, så du kan muligvis ved bedst få millisekundpræcision i praksis og sandsynligvis mindre), prøv at bruge date +%s.%N. (%N er nanosekunder siden hele andet sekund .)
  • @ChrisH Åh. Godt at påpege det; bash-aritmetisk udvidelse er kun heltal. Jeg ser to obvi ous muligheder enten grøft perioden i datoformatstrengen (og behandl den resulterende værdi som nanosekunder siden epoke), så brug date +%s%N, eller brug noget mere i stand som bc for at beregne den faktiske køretid ud fra de to værdier som jwchew antyder. Alligevel føler jeg, at denne tilgang er en suboptimal måde at gøre det på; time er betydeligt bedre, hvis det er tilgængeligt, af ovenstående grunde.
  • Bare installer bc, og gør derefter dette: runtime=$( echo "$end - $start" | bc -l )
  • Hvis dit script tager flere minutter, skal du bruge: echo "Duration: $((($(date +%s)-$start)/60)) minutes
  • Yderligere til @ rubo77 kommentar, hvis dit script tager flere timer, skal du bruge hours=$((runtime / 3600)); minutes=$(( (runtime % 3600) / 60 )); seconds=$(( (runtime % 3600) % 60 )); echo "Runtime: $hours:$minutes:$seconds (hh:mm:ss)"

Svar

Ring bare til times uden argumenter, når du afslutter dit script.

Med ksh eller zsh, du kan også bruge time i stedet. Med zsh vil time også give dig urets tid ud over brugeren og system CPU-tid.

For at bevare exit-status for dit script kan du gøre det:

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

Eller dig kan også tilføje en fælde på EXIT:

trap times EXIT 

På den måde kaldes tidspunkter, når skallen kommer ud og udgangsstatus bevares.

$ 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 

Bemærk også, at alle bash, ksh og zsh har en $SECONDS speciel variabel, der automatisk forøges hvert sekund. I både zsh og ksh93 kan variablen også gøres flydende punkt (med typeset -F SECONDS ) for at få mere præcision. Dette er kun vægurstid, ikke CPU-tid.

Kommentarer

  • Denne $ SECONDS-variabel er meget nyttig, tak!
  • Eliminerer din tilgang effekten af timelige begivenheder? – – Jeg tror, din tilgang er nær time -metoden, der blev præsenteret tidligere.- – Jeg tror timeit tilgang præsenteret i MATLAB-kode kan være nyttig her.
  • Hej, @St é phane Chazelas. Jeg fandt times giver ‘ ikke muren tid, ikke? for eksempel bash -c 'trap times EXIT;sleep 2'
  • @Binarus, ja, denne funktion kommer fra ksh. I modsætning til ksh93 / zsh udover bash, der ikke understøtter flydende punkt, er det ‘ også brudt i, at når du har indstillet SECONDS til 0, vil det skifte til 1 fra 0 til 1 sekund senere ( da det kun betragter resultatet af time() som kun har fuld anden granularitet).
  • @Binarus, ikke at ‘ s mellem 0 og 1. Hvis du indstiller SECONDS til 0 ved 12: 00: 00.000, ændres det til 1 et sekund senere. Men hvis du indstiller klokken 12.00: 00.999, ændres det til 1 en milisekund senere. Men ja, jeg er enig i, at det sandsynligvis ikke ‘ t betyder meget i praksis, hvis du ikke ‘ t alligevel har sub-sekunders præcision.

Svar

Jeg var lidt sent på vognen, men ville sende min løsning (for præcision under sekund ) hvis andre tilfældigvis snubler over denne tråd gennem søgning. Outputtet er i format af dage, timer, minutter og endelig sekunder:

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 

Håber nogen derude finder dette nyttigt!

Kommentarer

  • Jeg antager, at der ikke er nogen anden (kun bash) måde undtagen at bruge ‘ bc ‘ for at udføre beregningerne. BTW rigtig godt script;)
  • Pas på, at FreeBSD ‘ s date understøtter ikke præcision under sekundet og tilføjer bare bogstavelig “N” til tidsstemplet.
  • Meget flot script, men min bash ‘ t håndtere den sekundære del. Jeg lærte også, at / 1 i bc effektivt fjerner det, så jeg tilføjede @ / 1 @ til $ ds-beregningen, og det viser ting meget flot nu!
  • bc understøtter modulo: dt2=$(echo "$dt%86400" | bc). Bare siger … BTW Jeg foretrækker formularen dt2=$(bc <<< "${dt}%86400") men at ‘ er helt personlig.
  • Jeg don ‘ ikke ved noget om bc endnu, men delingen af 86400 gør mig mistroisk mod følgende årsag: Der er dage, der ikke har 86400 sekunder, for eksempel dage, hvor tiden skiftes fra sommertid til normal tid og omvendt eller dage med spring sekunder. Hvis sådanne dage er en del af det tidsrum, du vil beregne, dvs. ligger mellem res1 og res2 (inklusive disse værdier), er dit script vil sandsynligvis mislykkes.

Svar

Min metode til bash :

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

Kommentarer

  • Dette viser forløbet tid, men det gør det ikke ‘ t viser ” finkornet output som processortid, I / O-tid ” som anmodet om i spørgsmålet.
  • De, der søger efter et svar på det stillede spørgsmål, vil sandsynligvis finde denne løsning nyttig. Din kommentar ville være bedre adresseret til OPs-spørgsmålet.

Svar

Personligt kan jeg godt lide at pakke alle mine script kode i en eller anden “hoved” funktion som sådan:

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

Bemærk, hvor let det er at bruge kommandoen time i dette scenarie. Det er klart, at du ikke måler den nøjagtige tid inklusive script-analyse-tid, men jeg finder det nøjagtigt i de fleste situationer.

Kommentarer

  • Fantastisk løsning, kræver ikke ‘ ekstra værktøjer og er selvstændig.

Svar

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

Ja, dette kalder Python, men hvis du kan leve med det, er dette en ganske flot og kortfattet løsning.

Kommentarer

  • Pas på, at kørsel af python kommandoen i en subshell og læsning af dens output vil tage flere millioner nanosekunder på de fleste nuværende systemer. Samme for at køre date.
  • ” Flere millioner nanosekunder ” er flere millisekunder Folk, der timing bash-scripts, er normalt ikke meget bekymrede over det (medmindre de kører flere milliarder scripts pr. Megasekund).
  • Bemærk også, at beregning foretages inden for en pythonproces, værdierne blev samlet samlet, inden python blev påberåbt. Eksekveringstiden for scriptet måles korrekt uden ” millioner af nanosekunder ” overhead.
  • time main ovenfor er så meget mere rent …ja, vi kan leve med masser af nedbrydning, men det ‘ er bedre måde uden så lad ‘ forblive ingeniører!
  • $(echo $end - $start | bc) vil gøre det samme uden python

Svar

Dette spørgsmål er ret gammelt, men i forsøget på at finde min foretrukne måde at gøre det på kom denne tråd højt … og jeg overraskede ingen, der nævnte det:

perf stat -r 10 -B sleep 1 

“perf” er et præstationsanalyseværktøj inkluderet i kernen under “tools / perf” og ofte tilgængeligt til installation som en separat pakke (“perf” i CentOS og “linux-tools” på Debian / Ubuntu). Linux Kernal perf Wiki har meget mere information om det.

At køre “perf stat” giver en hel del detaljer inklusive gennemsnitlig udførelsestid lige i slutningen:

1.002248382 seconds time elapsed ( +- 0.01% ) 

Kommentarer

  • Hvad er perf?
  • @B Layer – redigeret mit svar t o give en kort beskrivelse af ‘ perf ‘.
  • perf stat nu klager over ” Du har muligvis ikke tilladelse til at indsamle statistik. ” medmindre du kører nogle kommandoer med sudo, hvilket gør det ubrugeligt i alle scenarier, hvor du ikke ‘ ikke helt ejer målmaskinen.
  • Fantastisk, tak! Meget mere finkornet løsning end time andre antyder. Især time er ikke anvendelig til måling af kodekonkurrenceløsninger (da det ikke ‘ t udskrivningstid i millisekunder og mindre) , mens din løsning gør det.

Svar

En lille skalfunktion, der kan tilføjes før kommandoer til at måle deres tid:

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

Brug det derefter i dit script eller på din kommandolinje sådan:

tm the_original_command with all its parameters 

Kommentarer

  • Det ville være værd at tilføje millisekunder, prøvet ilke s=$(date +%s000), ikke sikker hvis det virker.
  • @loretoparisi i milisekunder, kan du prøve date +%s%N. Se serverfault.com/questions/151109/…
  • Dette er fantastisk, undtagen mere sikkert brug "$@"

Svar

#!/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..." 

Kommentarer

  • Hvordan adskiller dette sig virkelig fra de andre svar, der allerede er givet, som bruger date før og efter scriptet og output forskellen mellem dem?

Svar

  1. Bare brug time [any command] . Eks: time sleep 1 vil sove i realtid (dvs. som tidsindstillet af et stopur) på ~ 1.000 til ~ 1.020 sek, som vist her:

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

    Hvilken smuk ting. Du kan lægge en hvilken som helst kommando efter den, og den outputter resultatet i en flot, menneskelig læsbar form. Jeg kan godt lide at bruge det til timing builds. Eks:

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

    … eller til git-operationer, som potentielt kan være virkelig lang, så jeg kan udvikle realistiske mentale forventninger:

      # 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. For mere tilpassede timingbehov hvor du muligvis skal manipulere output eller konvertere det til andre former, i bash , brug den interne $SECONDS variabel. Her “er en demo, herunder konvertering af disse sekunder til andre enheder, såsom minutter med flydende punkt:

    Bemærk, at dt_min afrundes fra 0.01666666666... (1 sekund = så mange minutter) til 0.017 i dette tilfælde da jeg bruger funktionen printf til at afrunde. sleep 1; -delen nedenfor er det sted, hvor du kalder dit script til at køre og tid, men jeg sover bare i 1 sekund i stedet for denne demo.

    Kommando:

      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"  

    Output:

      dt_sec = 1; dt_min = 0.017  

Relateret:

  1. Læs mere om bc og printf i mit svar her: https://stackoverflow.com/questions/12722095/how-do-i-use-floating-point-division-in-bash/58479867#58479867
  2. Jeg kan ikke huske, hvor jeg først lærte om kommandoen time, men det kan have været fra @Trudberts svar lige her .

Svar

Brug kun bash er det også muligt at måle og beregne tidsvarigheden for en del af shell-scriptet (eller den forløbne tid for hele scriptet):

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

du kan nu enten bare udskrive forskellen:

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

hvis du kun har brug for trinvis varighed, skal du gøre:

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

Du kan også gemme varigheden i en variabel:

let diff=end-start 

Kommentarer

  • ^ DETTE er svaret folkens. Eller mere simpelt: $ SECONDS i slutningen. Det ‘ er en INDBYGGET Bash-variabel. Alle de andre svar gør bare ekstra arbejde for at genopfinde dette hjul …
  • Dette er bare en gentagelse af Marks tidligere svar unix.stackexchange .com / a / 340156/155362 .

Svar

Timing-funktion baseret på SECONDS, kun begrænset til granularitet på andet niveau, bruger ikke eksterne kommandoer:

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" } 

For eksempel:

time_it sleep 5 

giver

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

Svar

Brug bash-tid indbygget?

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. 

Eksempel:

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

Output:

The command took 0.108s 

Svar

Her “er en variation af Alex “s svar. Jeg er kun interesseret i minutter og sekunder, men jeg ville også have det formateret anderledes. Så jeg gjorde dette:

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

Kommentarer

  • Hvorfor downvote? Har jeg en fejl?
  • Brug af python til denne beregning ser ligesom en fejl ud for mig, undskyld mand. Hvorfor folk kan ‘ ikke bare ringe til time er uden for mig.

Svar

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

Svar

Den accepterede løsning ved hjælp af time skriver til stderr.
Løsningen ved hjælp af times skriver til stdout.
Løsningerne, der bruger $SECONDS, mangler præcision i sekund.
De andre løsninger involverer at kalde eksterne programmer som date eller perf som ikke er effektiv.
Hvis du har det godt med nogen af disse, skal du bruge det.

Men hvis du har brug for en effektiv løsning for at få tiden med millisekund præcision og har brug for dem i variabler sådan at original output forbliver uforstyrret du kan kombinere proceserstatning med nogle omdirigeringer omkring time hvilket er meget hurtigere end at ringe eksterne programmer og tillader omdirigeringer omkring timing wrapper-scriptet som på den originale kommando / 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 

Vælg en af følgende varianter, der analyserer output af time passende til dine behov:
Korteste variant, hvor stdout af $Cmd skrives til stderr og intet til stdout:

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

Længere variant, der holder original stdout og stderr adskilt af hinanden:

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

Den mest komplicerede variant inkluderer lukning af de ekstra filbeskrivelser, så $Cmd kaldes som om uden denne timingindpakning omkring den og lvm kommandoer som vgs klager ikke over lækkede filbeskrivelser:

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

Dig kan endda falske en flydende tilføjelse i bash uden at ringe til bc hvilket ville være meget langsommere:

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 

Mulige output med de to eksempler på $Cmd på en langsom 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 

Eller:

stdout stderr CPU 0.008 s, Elapsed 0.109 s 

Skriv et svar

Din e-mailadresse vil ikke blive publiceret. Krævede felter er markeret med *