Hvordan få utføringstid for et skript effektivt?

Jeg vil vise fullføringstiden for et skript.

Det jeg for øyeblikket gjø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 slutt på skriptet. mulig å vise en finkornet utgang som prosessortid / io tid osv.?

Kommentarer

Svar

Jus t bruk time når du ringer til skriptet:

time yourscript.sh 

Kommentarer

  • Dette sendes ut tre ganger: real, user og sys. For betydningen av disse, se her .
  • og » ekte » er sannsynligvis det folk vil vite – » Real er tid for klokke – tid fra start til slutt for samtalen »
  • Er det en måte å fange stdout i en fil? For eksempel, noe sånt som time $( ... ) >> out.txt
  • Du kan også gjøre dette til kommandosekvenser på kommandolinjen, f.eks: time (command1 && command2)
  • Eller han kan bare gjøre i skriptet sitt: ekko $ SECONDS forutsatt at det er bash ….

Svar

Hvis time ikke er et alternativ,

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

Kommentarer

  • Merk at dette bare fungerer hvis du ikke trenger ‘ ikke trenger presisjon i andre sekund. For noen bruksområder kan det være akseptabelt , for andre ikke. For litt bedre presisjon (du ‘ fremkaller fortsatt date for eksempel to ganger, slik at du kan gå til best få millisekund presisjon i praksis, og sannsynligvis mindre), prøv å bruke date +%s.%N. (%N er nanosekunder siden hele andre .)
  • @ChrisH Å. Bra å påpeke det; bash-aritmetisk utvidelse er bare heltall. Jeg ser to obvi andre alternativer; enten grøft perioden i datoformatstrengen (og behandle den resulterende verdien som nanosekunder siden epoken), så bruk date +%s%N, eller bruk noe mer i stand som bc for å beregne den faktiske kjøretiden fra de to verdiene som jwchew antyder. Likevel føler jeg at denne tilnærmingen er en suboptimal måte å gjøre det på; time er betydelig bedre hvis tilgjengelig, av grunner som er skissert ovenfor.
  • Bare installer bc og gjør dette: runtime=$( echo "$end - $start" | bc -l )
  • Hvis skriptet ditt tar flere minutter, bruk: echo "Duration: $((($(date +%s)-$start)/60)) minutes
  • Videre til @ rubo77 kommentar, hvis skriptet ditt tar flere timer, bruk hours=$((runtime / 3600)); minutes=$(( (runtime % 3600) / 60 )); seconds=$(( (runtime % 3600) % 60 )); echo "Runtime: $hours:$minutes:$seconds (hh:mm:ss)"

Svar

Bare ring times uten argumenter når du avslutter skriptet.

Med ksh eller zsh, kan du også bruke time i stedet. Med zsh vil time også gi deg tid til veggklokken i tillegg til brukeren og system CPU-tid.

For å bevare utgangsstatusen for skriptet ditt, kan du gjøre det:

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

Eller du kan også legge til en felle på EXIT:

trap times EXIT 

På den måten vil tider bli kalt når skallet kommer ut og utgangsstatusen vil bli bevart.

$ 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 

Legg også merke til at alle bash, ksh og zsh har en $SECONDS spesiell variabel som automatisk økes hvert sekund. I både zsh og ksh93 kan den variabelen også gjøres flytende (med typeset -F SECONDS ) for å få mer presisjon. Dette er bare veggklokketid, ikke CPU-tid.

Kommentarer

  • At $ SECONDS-variabelen er veldig nyttig, takk!
  • Eliminerer din tilnærming effekten av timelige hendelser? – – Jeg tror tilnærmingen din er i nærheten av time tilnærming presentert tidligere.- – Jeg tror timeit tilnærmingen presentert i MATLAB-kode kan være nyttig her.
  • Hei, @St é phane Chazelas. Jeg fant times gir ‘ ikke veggen tid, ikke sant? for eksempel bash -c 'trap times EXIT;sleep 2'
  • @Binarus, ja, den funksjonen kommer fra ksh. I motsetning til ksh93 / zsh, ved siden av bash som ikke støtter flytende punkt, er det ‘ også brutt i at etter at du har satt SECONDS til 0, vil den endres til 1 fra 0 til 1 sekund senere ( da det bare vurderer resultatet av time() som bare har full andre granularitet).
  • @Binarus, ikke at ‘ s mellom 0 og 1. Hvis du setter SECONDS til 0 ved 12: 00: 00.000, vil det endres til 1 ett sekund senere. Men hvis du setter den klokken 12.00: 00.999, vil den endres til 1 milisekund senere. Men ja, jeg er enig i at det sannsynligvis ikke ‘ t betyr mye i praksis hvis du ikke har ‘ uansett. li>

Svar

Jeg var litt sent ute i vognen, men ville legge ut løsningen min (for presisjon i andre sekund ) hvis andre tilfeldigvis snubler over denne tråden gjennom søk. Utdataene er i format av dager, timer, minutter og til slutt 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åper noen der ute finner dette nyttig!

Kommentarer

  • Jeg antar at det ikke er noen annen (bare bash) måte bortsett fra å bruke ‘ bc ‘ for å gjøre beregningene. BTW veldig bra skript;)
  • Vær oppmerksom på at FreeBSD ‘ s date støtter ikke presisjon under sekund og vil bare legge til bokstavelig “N” til tidsstempelet.
  • Veldig fint skript, men min bash gjør det ikke ‘ t håndtere den andre delen. Jeg lærte også at / 1 i bc effektivt striper det, så jeg la @ / 1 @ til $ ds-beregningen, og det viser ting veldig fine nå!
  • bc støtter modulo: dt2=$(echo "$dt%86400" | bc). Bare å si … BTW Jeg foretrekker skjemaet dt2=$(bc <<< "${dt}%86400") men at ‘ er helt personlig.
  • Jeg don ‘ vet ikke noe om bc ennå, men delingen av 86400 gjør meg mistroisk mot følgende årsak: Det er dager som ikke har 86400 sekunder, for eksempel dager der tiden byttes fra sommertid til normal tid og omvendt, eller dager med sprangsekunder. Hvis slike dager er en del av tidsperioden du vil beregne, dvs. er mellom res1 og res2 (inkludert disse verdiene), skriptet ditt vil sannsynligvis mislykkes.

Svar

Min metode for 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øpt tid, men det ‘ t viser » finkornet utgang som prosessortid, I / O-tid » som forespurt i spørsmålet.
  • De som søker etter svar på spørsmålet som stilles, vil sannsynligvis finne denne løsningen nyttig. Kommentaren din vil være bedre adressert til OPs-spørsmålet.

Svar

Personlig liker jeg å pakke inn alle mine skriptkode i noen «hoved» -funksjoner slik:

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

Legg merke til hvor enkelt det er å bruke kommandoen time i dette scenariet. Åpenbart måler du ikke den nøyaktige tiden inkludert analysetid for skript, men jeg synes det er nøyaktig nok i de fleste situasjoner.

Kommentarer

  • Flott løsning, krever ikke ‘ ekstra verktøy og er selvstendig.

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 kaller Python, men hvis du kan leve med det, er dette en ganske fin, kortfattet løsning.

Kommentarer

  • Vær oppmerksom på at å kjøre den python kommandoen i en subshell og lese dens utdata vil ta flere millioner nanosekunder på de fleste nåværende systemer. Samme for å kjøre date.
  • » Flere millioner nanosekunder » er flere millisekunder Folk timing bash-skript er vanligvis ikke veldig bekymret for det (med mindre de kjører flere milliarder skript per megasekund).
  • Legg også merke til det, selv om beregning gjøres inne i en pythonprosess, verdiene ble samlet helt før python ble påkalt. Gjennomføringstiden for skriptet vil bli målt riktig, uten » millioner av nanosekunder » overhead.
  • time main over er så mye mer rent …ja, vi kan leve med mye nedbrytning, men det ‘ er bedre uten å la ‘ være ingeniører!
  • $(echo $end - $start | bc) vil gjøre det samme uten python

Svar

Dette spørsmålet er ganske gammelt, men når jeg prøver å finne min favoritt måte å gjøre det på, kom denne tråden høyt … og jeg overrasket ingen nevnte det:

perf stat -r 10 -B sleep 1 

«perf» er et ytelsesanalyseringsverktøy inkludert i kjernen under «tools / perf» og ofte tilgjengelig for installasjon som en egen pakke («perf» i CentOS og «linux-tools» på Debian / Ubuntu). Linux Kernal perf Wiki har mye mer informasjon om det.

Å kjøre «perf stat» gir ganske mange detaljer inkludert gjennomsnittlig utførelsestid rett på slutten:

1.002248382 seconds time elapsed ( +- 0.01% ) 

Kommentarer

  • Hva er perf?
  • @B Layer – redigerte svaret mitt t o gi en kort beskrivelse av ‘ perf ‘.
  • perf stat klager nå over » Du har kanskje ikke tillatelse til å samle inn statistikk. » med mindre du kjører noen kommandoer med sudo, noe som gjør det ubrukelig i alle scenarier der du ikke ‘ t helt egen målmaskin.
  • Fantastisk, takk! Mye mer finkornet løsning enn time andre antyder. Spesielt time er ikke aktuelt for måling av kodekonkurranseløsninger (da det ikke ‘ t utskriftstid i millisekunder og mindre) , mens løsningen din gjør det.

Svar

En liten skallfunksjon som kan legges til før kommandoer for å måle tiden deres:

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

Bruk den deretter i skriptet, eller på kommandolinjen slik:

tm the_original_command with all its parameters 

Kommentarer

  • Det ville være verdt å legge til millisekunder, prøvde ilke s=$(date +%s000), ikke sikker hvis det fungerer.
  • @loretoparisi i milisekunder, kan du prøve date +%s%N. Se serverfault.com/questions/151109/…
  • Dette er flott, bortsett fra tryggere bruk "$@"

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 er dette egentlig forskjellig fra de andre svarene som allerede er gitt som bruker date før og etter skriptet og skrive ut forskjellen mellom dem?

Svar

  1. Bare bruk time [any command] . Eks: time sleep 1 vil sove i sanntid (dvs. som tidsbestemt av en stoppeklokke) på ~ 1.000 til ~ 1.020 sek, som vist her:

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

    For en vakker ting. Du kan legge en hvilken som helst kommando etter den, og den viser resultatet i en fin, lesbar form. Jeg liker veldig godt å bruke den til timingbygging. Eks:

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

    … eller for git-operasjoner som potensielt kan være virkelig lenge, så jeg kan utvikle 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 mer tilpassede timingbehov der du kanskje trenger å manipulere utdata eller konvertere den til andre former, i bash , bruk den interne $SECONDS variabelen. Her «er en demo, inkludert konvertering av disse sekundene til andre enheter, for eksempel minutter med flytende punkt:

    Merk at dt_min blir avrundet fra 0.01666666666... (1 sekund = så mange minutter) til 0.017 i dette tilfellet siden jeg bruker printf -funksjonen til å runde. sleep 1; -delen nedenfor er der du vil kalle skriptet ditt til å kjøre og tid, men jeg sover bare i 1 sekund i stedet for denne demoens skyld.

    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"  

    Utgang:

      dt_sec = 1; dt_min = 0.017  

Relatert:

  1. Les mer om bc og printf i mitt svar her: https://stackoverflow.com/questions/12722095/how-do-i-use-floating-point-division-in-bash/58479867#58479867
  2. Jeg husker ikke hvor jeg først lærte om time kommandoen lenger, men det kan ha vært fra @Trudbert «svar akkurat her .

Svar

Bruk bare bash er det også mulig å måle og beregne tidsvarigheten for en del av skallskriptet (eller forløpt tid for hele skriptet):

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

du kan nå enten bare skrive ut forskjellen:

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

hvis du bare trenger trinnvis varighet, gjør:

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

Du kan også lagre varigheten i en variabel:

let diff=end-start 

Kommentarer

  • ^ DETTE er svaret folkens. Eller enklere: $ SECONDS på slutten. Det ‘ er en INNBYGGET Bash-variabel. Alle de andre svarene gjør bare ekstra arbeid for å finne opp dette hjulet på nytt …
  • Dette er bare en repetisjon av Marks tidligere svar unix.stackexchange .com / a / 340156/155362 .

Svar

Timingsfunksjon basert på SECONDS, begrenset til bare andre nivå granularitet, bruker 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 

gir

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

Svar

Bruk bash tid innebygd?

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 } 

Utgang:

The command took 0.108s 

Svar

Her «er en variant av Alex «s svar. Jeg bryr meg bare om minutter og sekunder, men jeg ville også ha det formatert annerledes. Så jeg gjorde dette:

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

Kommentarer

  • Hvorfor nedstemmen? Har jeg en feil?
  • Å bruke python for den beregningen ser ut som en feil for meg heller, beklager mann. Hvorfor folk ikke kan ‘ ikke bare ringe time er utenfor meg.

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 aksepterte løsningen ved hjelp av time skriver til stderr.
Løsningen ved hjelp av times skriver til stdout.
Løsningene som bruker $SECONDS mangler presisjon i andre sekund.
De andre løsningene innebærer å ringe eksterne programmer som date eller perf som ikke er effektiv.
Hvis du har det bra med noen av disse, bruk den.

Men hvis du trenger en effektiv løsning for å få tidene med millisekund presisjon og trenger dem til variabler slik at originalutdata forblir uforstyrret du kan kombinere prosesserstatning med noen omdirigeringer rundt time som er mye raskere enn å ringe eksterne programmer og tillater omdirigeringer rundt tidsinnpakningsskriptet som på den opprinnelige kommandoen / skriptet.

# 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 

Velg en av følgende varianter som analyserer utdata fra time passende for deg behov:
Korteste variant der stdout av $Cmd skrives til stderr og ingenting å stdout:

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

Lengre variant som holder original stdout og stderr atskilt fra hverandre:

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

Den mest kompliserte varianten inkluderer å lukke de ekstra filbeskrivelsene slik at $Cmd kalles som om uten denne tidsinnpakningen rundt den og lvm kommandoer som vgs klager ikke på lekkete filbeskrivere:

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

Du kan til og med falske et flytende tillegg i bash uten å ringe bc som ville være mye tregere:

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 utganger med de to eksemplene på $Cmd på en treg 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 

Legg igjen en kommentar

Din e-postadresse vil ikke bli publisert. Obligatoriske felt er merket med *