Bagaimana cara mendapatkan waktu eksekusi skrip secara efektif?

341

Saya ingin menampilkan waktu penyelesaian skrip.

Apa yang saya lakukan saat ini adalah -

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

Ini hanya menunjukkan waktu awal dan akhir skrip. Apakah mungkin untuk menampilkan output berbutir halus seperti waktu prosesor / waktu, dll?

mtk
sumber
2
stackoverflow.com/questions/385408/…
Ciro Santilli 新疆 改造 中心 法轮功 六四 事件
Saya pikir jawaban yang diterima saat ini tidak benar-benar cukup karena fluktuasi temporal.
Léo Léopold Hertz 준영
1
@CiroSantilli 烏坎 事件 2016 六四 事件 法轮功 Saya pikir utas yang Anda usulkan masih tidak cukup untuk menghilangkan efek dari peristiwa temporal.
Léo Léopold Hertz 준영
Ada perintah yang dapat Anda jalankan dan kemudian perintah unix itu otomatis kali semua, lupa cara mengaktifkannya?
powder366

Jawaban:

450

Cukup gunakan timesaat Anda memanggil skrip:

time yourscript.sh
Trudbert
sumber
61
Ini output tiga kali: real, userdan sys. Untuk maknanya, lihat di sini .
Garrett
27
dan "nyata" mungkin adalah apa yang ingin diketahui orang - "Nyata adalah jam dinding waktu - waktu dari awal hingga akhir panggilan"
Brad Parks
3
Apakah ada cara untuk menangkap stdout ke dalam file? Misalnya, sesuatu sepertitime $( ... ) >> out.txt
Jon
1
Anda juga dapat melakukan ini pada urutan perintah di baris perintah, misalnya:time (command1 && command2)
meetar
1
Atau dia bisa, dalam skripnya, lakukan saja: echo $ SECONDS dengan asumsi itu bash ....
Crossfit_and_Beer
170

Jika timebukan opsi,

start=`date +%s`
stuff
end=`date +%s`

runtime=$((end-start))
Rob Bos
sumber
30
Perhatikan bahwa ini hanya berfungsi jika Anda tidak membutuhkan ketepatan kedua. Untuk beberapa penggunaan yang mungkin dapat diterima, untuk yang lain tidak. Untuk presisi sedikit lebih baik (Anda masih memohon datedua kali, misalnya, sehingga Anda mungkin di terbaik mendapatkan milidetik presisi dalam praktek, dan mungkin kurang), coba gunakan date +%s.%N. ( %NAdalah nanoseconds sejak seluruh detik.)
CVn
Poin bagus. Saya memikirkan hal itu setelah meninggalkan keyboard tetapi tidak kembali. ^^ Ingat juga, OP, "tanggal" itu sendiri akan menambahkan beberapa milidetik pada waktu berjalan.
Rob Bos
2
@ Chris Oh. Bagus menunjukkannya; ekspansi aritmatika bash hanya bilangan bulat. Saya melihat dua opsi yang jelas; baik membuang periode dalam string format tanggal (dan memperlakukan nilai yang dihasilkan sebagai nanodetik sejak zaman), jadi gunakan date +%s%N, atau gunakan sesuatu yang lebih mampu bcmenghitung runtime aktual dari dua nilai seperti yang disarankan jwchew. Namun, saya merasa pendekatan ini adalah cara yang kurang optimal untuk melakukannya; timejauh lebih baik jika tersedia, untuk alasan yang diuraikan di atas.
CVn
10
Cukup instal bcdan kemudian lakukan ini:runtime=$( echo "$end - $start" | bc -l )
redolent
10
Jika skrip Anda membutuhkan beberapa Menit, gunakan:echo "Duration: $((($(date +%s)-$start)/60)) minutes
rubo77
75

Panggil saja timestanpa argumen saat keluar dari skrip Anda.

Dengan kshatau zsh, Anda juga bisa menggunakannya time. Dengan zsh, timejuga akan memberi Anda waktu jam dinding selain waktu pengguna dan sistem CPU.

Untuk mempertahankan status keluar dari skrip Anda, Anda dapat membuatnya:

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

Atau Anda juga dapat menambahkan jebakan pada EXIT:

trap times EXIT

Dengan begitu, waktu akan dipanggil setiap kali shell keluar dan status keluar akan dipertahankan.

$ 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

Perhatikan juga bahwa semua bash, kshdan zshmemiliki $SECONDSvariabel khusus yang secara otomatis bertambah setiap detik. Di kedua zshdan ksh93, variabel itu juga bisa dibuat floating point (dengan typeset -F SECONDS) untuk mendapatkan lebih presisi. Ini hanya jam dinding, bukan waktu CPU.

Stéphane Chazelas
sumber
12
Variabel $ DETIK itu sangat berguna, terima kasih!
andybuckley
Apakah pendekatan Anda menghilangkan efek peristiwa temporal? - - Saya pikir pendekatan Anda mendekati timependekatan yang disajikan sebelumnya. - - Saya pikir timeitpendekatan yang disajikan dalam kode MATLAB dapat bermanfaat di sini.
Léo Léopold Hertz 준영
2
Hai, @ Stéphane Chazelas. Saya menemukan timestidak memberi waktu dinding, kan? misalnya,bash -c 'trap times EXIT;sleep 2'
user15964
32

Saya agak terlambat untuk ikut-ikutan, tetapi ingin memposting solusi saya (untuk presisi sub-detik) kalau-kalau ada orang lain yang tersandung pada thread ini melalui pencarian. Outputnya dalam format hari, jam, menit, dan akhirnya detik:

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)

printf "Total runtime: %d:%02d:%02d:%02.4f\n" $dd $dh $dm $ds

Semoga seseorang di luar sana menemukan ini berguna!

jwchew
sumber
1
Saya kira tidak ada cara lain (hanya bash) kecuali menggunakan 'bc' untuk melakukan perhitungan. Script BTW benar-benar bagus;)
tvl
1
Waspadalah bahwa FreeBSD datetidak mendukung ketepatan sub-detik dan hanya akan menambahkan " N" literal ke stempel waktu.
Anton Samsonov
1
Skrip yang sangat bagus, tetapi bash saya tidak menangani bagian subsecond. Saya juga belajar, bahwa / 1 dalam bc secara efektif menghapus itu, jadi saya menambahkan @ / 1 @ ke perhitungan $ ds, dan menampilkan hal-hal yang sangat bagus sekarang!
Daniel
1
bc mendukung modulo: dt2=$(echo "$dt%86400" | bc). Hanya mengatakan ... Btw saya lebih suka bentuk dt2=$(bc <<< "${dt}%86400")tapi itu sepenuhnya pribadi.
Dani_l
16

Metode saya untuk bash:

# Reset BASH time counter
SECONDS=0
    # 
    # do stuff
    # 
ELAPSED="Elapsed: $(($SECONDS / 3600))hrs $((($SECONDS / 60) % 60))min $(($SECONDS % 60))sec"
Menandai
sumber
Ini memang menunjukkan waktu yang berlalu, tetapi tidak menunjukkan "output berbutir halus seperti waktu prosesor, waktu I / O" seperti yang diminta dalam pertanyaan.
roaima
3
Mereka yang mencari jawaban atas pertanyaan yang diajukan cenderung menemukan solusi ini berguna. Komentar Anda akan lebih baik ditujukan pada pertanyaan OP.
Tandai
5
#!/bin/bash
start=$(date +%s.%N)

# HERE BE CODE

end=$(date +%s.%N)    
runtime=$(python -c "print(${end} - ${start})")

echo "Runtime was $runtime"

Ya, ini panggilan Python, tetapi jika Anda bisa hidup dengan itu maka ini solusi yang cukup bagus.

Alex
sumber
5
Berhati-hatilah bahwa menjalankan pythonperintah itu dalam subkulit dan membaca outputnya akan membutuhkan beberapa juta nanodetik pada kebanyakan sistem saat ini. Sama untuk berlari date.
Stéphane Chazelas
7
"Beberapa jutaan nanodetik" adalah beberapa milidetik. Orang-orang mengatur waktu skrip bash biasanya tidak terlalu peduli tentang hal itu (kecuali jika mereka menjalankan beberapa miliar skrip per megasecond).
Zilk
2
Perhatikan juga bahwa, bahkan jika perhitungan dilakukan di dalam proses python, nilai-nilai seluruhnya dikumpulkan sebelum python dipanggil. Waktu pelaksanaan skrip akan diukur dengan benar, tanpa overhead "jutaan nanodetik".
Victor Schröder
5

Pertanyaan ini cukup lama tetapi dalam mencoba menemukan cara favorit saya untuk melakukannya, utas ini muncul tinggi ... dan saya terkejut tidak ada yang menyebutkannya:

perf stat -r 10 -B sleep 1

'perf' adalah alat analisis kinerja yang termasuk dalam kernel di bawah 'tools / perf' dan sering tersedia untuk diinstal sebagai paket terpisah ('perf' di CentOS dan 'linux-tools' di Debian / Ubuntu). Linux Kernal perf Wiki memiliki lebih banyak informasi tentangnya.

Menjalankan 'stat stat' memberikan sedikit detail termasuk waktu eksekusi rata-rata tepat di akhir:

1.002248382 seconds time elapsed                   ( +-  0.01% )
Zaahid
sumber
2
Apa perf?
B Layer
@ B Layer - edit jawaban saya untuk memberikan deskripsi singkat tentang 'perf'.
Zaahid
1
perf statsekarang mengeluh tentang "Anda mungkin tidak memiliki izin untuk mengumpulkan statistik." kecuali Anda menjalankan beberapa perintah sudo, yang membuatnya tidak berguna di semua skenario di mana Anda tidak sepenuhnya memiliki mesin target.
alamar
4

Fungsi shell kecil yang dapat ditambahkan sebelum perintah untuk mengukur waktu mereka:

tm() {
  s=$(date +%s)
  $@
  rc=$?
  echo "took $[$(date +%s)-$s] seconds. return code $rc" >&2
  return $rc
}

Kemudian gunakan dalam skrip Anda, atau pada baris perintah Anda seperti ini:

tm the_original_command with all its parameters
Evgeny
sumber
Layak untuk menambahkan milidetik, coba ilke s=$(date +%s000), tidak yakin apakah itu berfungsi.
loretoparisi
3

Berikut variasi jawaban Alex. Saya hanya peduli menit dan detik, tetapi saya juga ingin itu diformat berbeda. Jadi saya melakukan ini:

start=$(date +%s)
end=$(date +%s)
runtime=$(python -c "print '%u:%02u' % ((${end} - ${start})/60, (${end} - ${start})%60)")
mpontillo
sumber
1
Mengapa downvote? Apakah saya punya bug?
mpontillo
3
#!/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..."
Shicheng Guo
sumber
7
Bagaimana ini benar-benar berbeda dari jawaban lain yang sudah diberikan yang digunakan datesebelum dan sesudah skrip dan menghasilkan perbedaan di antara mereka?
Eric Renouf
2

Dengan hanya menggunakan bash, juga dimungkinkan untuk mengukur dan menghitung durasi waktu untuk sebagian skrip shell (atau waktu yang telah berlalu untuk seluruh skrip):

start=$SECONDS

... # do time consuming stuff

end=$SECONDS

Anda sekarang dapat mencetak perbedaannya:

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

jika Anda hanya membutuhkan durasi tambahan, lakukan:

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

Anda juga dapat menyimpan durasi dalam suatu variabel:

let diff=end-start
gauteh
sumber
^ INI adalah jawabannya. Atau lebih sederhana: $ DETIK pada akhirnya. Ini adalah variabel BILT-IN Bash. Semua jawaban lain hanya melakukan pekerjaan ekstra untuk menemukan kembali roda ini ...
Crossfit_and_Beer
2

Secara pribadi, saya suka membungkus semua kode skrip saya di beberapa fungsi "utama" seperti:

main () {
 echo running ...
}

# stuff ...

# calling the function at the very end of the script
time main

Perhatikan betapa mudahnya menggunakan timeperintah dalam skenario ini. Jelas Anda tidak mengukur waktu yang tepat termasuk waktu parse skrip, tapi saya merasa cukup akurat dalam kebanyakan situasi.

LeZuse
sumber
1
#!/bin/bash
begin=$(date +"%s")

Script

termin=$(date +"%s")
difftimelps=$(($termin-$begin))
echo "$(($difftimelps / 60)) minutes and $(($difftimelps % 60)) seconds elapsed for Script Execution."
Arun Binoy
sumber
1

Fungsi pengaturan waktu berdasarkan SECONDS, terbatas hanya pada granularity tingkat kedua, tidak menggunakan perintah eksternal apa pun:

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

Sebagai contoh:

time_it sleep 5

memberi

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