Alternatif tolok ukur `waktu` non-bash universal? [Tutup]

10

Untuk membandingkan menjalankan kali skrip antara kerang yang berbeda, beberapa jawaban SE menyarankan menggunakan bash's built-in time perintah, seperti:

time bash -c 'foo.sh'
time dash -c 'foo.sh'

... dll , untuk setiap shell untuk diuji. Tolok ukur seperti itu gagal menghilangkan waktu yang dibutuhkan setiap shell untuk memuat dan menginisialisasi sendiri . Sebagai contoh, misalkan kedua perintah di atas disimpan pada perangkat yang lambat dengan kecepatan baca dari floppy disk awal , (124KB / dtk), dash(a ~ 150K dieksekusi) akan memuat sekitar 7x lebih cepat daripada bash( ~ 1M ), shell waktu pemuatan akan memiringkan timeangka - waktu pra-pemuatan kerang-kerang tersebut tidak relevan untuk mengukur waktu proses di foo.shbawah setiap kerang setelah kerang dimuat.

Apa kegunaan portabel dan umum terbaik untuk menjalankan skrip waktu yang dapat dijalankan dari dalam setiap shell? Jadi kode di atas akan terlihat seperti:

bash -c 'general_timer_util foo.sh'
dash -c 'general_timer_util foo.sh'

NB: tidak ada perintah built-in shell time, karena tidak ada yang portabel atau umum.


Lebih baik lagi jika util juga dapat membuat tolok ukur waktu yang diambil oleh perintah internal dan jaringan pipa shell, tanpa pengguna harus terlebih dahulu membungkusnya dalam skrip. Sintaks buatan seperti ini akan membantu:

general_timer_util "while read x ; do echo x ; done < foo"

Beberapa kerang timedapat mengatur ini. Misalnya bash -c "time while false ; do : ; done"berfungsi. Untuk melihat apa yang berfungsi, (dan tidak) pada sistem Anda, coba:

tail +2 /etc/shells | 
while read s ; do 
    echo $s ; $s -c "time while false ; do : ; done" ; echo ----
done
agc
sumber
6
Cukup gunakan /usr/bin/time?
Kusalananda
1
Saya tidak mengerti bagaimana setiap non-builtin mungkin baik "menghilangkan waktu yang dibutuhkan untuk setiap shell untuk beban dan menginisialisasi itu sendiri" dan menjalankan script mandiri sementara "portabel dan umum".
Michael Homer
1
Itu bukan jawaban untuk pertanyaan itu, ini merupakan prompt bagi Anda untuk mengklarifikasi apa yang Anda inginkan.
Michael Homer
1
Saya telah memposting upaya terbaik saya, tetapi saya pikir pertanyaannya masih belum ditentukan secara pasti tentang apa yang sebenarnya ingin dicapai.
Michael Homer
2
Apa yang Anda maksud dengan "portabel atau umum"? Shell builtin sama portabelnya (bekerja pada banyak sistem) dan lebih umum (bekerja dalam lebih banyak keadaan, karena mereka dapat mengatur waktu selain eksekusi file) sebagai perintah eksternal. Masalah apa yang Anda sedang coba pecahkan?
Gilles 'SO- berhenti menjadi jahat'

Jawaban:

10

Anda harus mencatat bahwa timeditentukan oleh POSIX , dan AFAICT satu-satunya opsi yang POSIX sebutkan ( -p) didukung dengan benar oleh berbagai shell:

$ bash -c 'time -p echo'

real 0.00
user 0.00
sys 0.00
$ dash -c 'time -p echo'

real 0.01
user 0.00
sys 0.00
$ busybox sh -c 'time -p echo'

real 0.00
user 0.00
sys 0.00
$ ksh -c 'time -p echo'       

real 0.00
user 0.00
sys 0.00
muru
sumber
1
Masalahnya adalah bahwa untuk dapat membandingkan pengaturan waktu, hasilnya harus diatur dengan implementasi yang sama dari time. Ini sebanding dengan membiarkan pelari cepat mengukur waktu mereka sendiri pada 100m, secara individual, daripada melakukannya dengan satu jam pada waktu yang sama. Ini jelas-jelas membingungkan, tapi tetap saja ...
Kusalananda
@ Kusalananda Saya pikir masalahnya adalah bahwa OP pikir timeitu tidak portabel; tampaknya portabel. (Saya setuju dengan poin Anda tentang komparatif)
muru
@muru, pada sistem saya dash -c 'time -p while false ; do : ; done'mengembalikan "waktu: tidak dapat berjalan sementara: Tidak ada file atau direktori seperti itu <cr> Perintah keluar dengan kesalahan status 127" .
agc
1
@agc POSIX juga mengatakan: "Utilitas istilah digunakan, bukan perintah, untuk menyoroti fakta bahwa perintah majemuk shell, pipa, built-in khusus, dan sebagainya, tidak dapat digunakan secara langsung. Namun, utilitas mencakup program aplikasi pengguna dan skrip shell, bukan hanya utilitas standar. " (lihat bagian RATIONALE)
muru
7

Saya menggunakan perintah GNU date , yang mendukung timer resolusi tinggi:

START=$(date +%s.%N)
# do something #######################

"$@" &> /dev/null

#######################################
END=$(date +%s.%N)
DIFF=$( echo "scale=3; (${END} - ${START})*1000/1" | bc )
echo "${DIFF}"

Dan kemudian saya memanggil skrip seperti ini:

/usr/local/bin/timing dig +short unix.stackexchange.com
141.835

Unit output dalam milidetik.

Rabin
sumber
1
Dengan asumsi waktu (waktu zaman) tidak berubah di antara keduanya. Tidak bisa memikirkan kasus dalam praktiknya di mana ia akan menyebabkan masalah tetapi masih layak disebut.
phk
1
Anda harus menambahkan bahwa ini memerlukan GNU datekhusus.
Kusalananda
@ php tolong jelaskan?
Rabin
1
@Rabin Katakanlah klien NTP Anda masalah dan perbarui dan ubah jam Anda di mana STARTdan ENDdiatur, maka ini jelas akan mempengaruhi hasil Anda. Tidak tahu seberapa tepat Anda membutuhkannya dan apakah itu penting dalam kasus Anda, tetapi seperti yang saya katakan, sesuatu yang perlu diingat. (Kisah menyenangkan: Saya tahu sebuah perangkat lunak di mana tepatnya itu mengarah pada hasil negatif yang tak terduga - digunakan untuk menghitung perhitungan - yang kemudian memecahkan beberapa hal.)
phk
1
Juga, bukankah beberapa klien NTP memperlambat dan mempercepat jam, daripada membuat "lompatan" dalam waktu sistem? Jika Anda memiliki klien NTP seperti itu, dan Anda melakukan beberapa pengaturan kemarin malam, mereka mungkin condong oleh klien NTP "mengantisipasi" lompatan kedua. (Atau apakah sistem jam hanya menjalankan ke 61 dalam kasus itu?)
Jörg W Mittag
6

The timeutilitas biasanya dibangun ke shell, seperti yang Anda telah memperhatikan, yang membuatnya berguna sebagai timer "netral".

Namun, utilitas biasanya juga tersedia sebagai utilitas eksternal /usr/bin/time,, yang dapat digunakan untuk melakukan eksperimen pengaturan waktu yang Anda usulkan.

$ bash -c '/usr/bin/time foo.sh'
Kusalananda
sumber
Bagaimana ini "menghilangkan waktu yang dibutuhkan untuk setiap shell untuk memuat dan menginisialisasi sendiri"?
Michael Homer
1
Jika foo.shdieksekusi dan memiliki shebang, maka ini selalu menjalankannya dengan shell yang sama, dan ia menghitung waktu startup shell itu, jadi ini bukan yang diinginkan OP. Jika foo.shsalah satu dari itu hilang, maka ini tidak berfungsi sama sekali.
Kevin
@ Kevin Sangat benar. Sepertinya saya hanya timemempertimbangkan "no built-in shell ". Waktu startup shell mungkin harus diukur secara terpisah.
Kusalananda
1
Saya tidak tahu ada shell yang memiliki timeperintah builtin. Namun banyak kerang termasuk bashmemiliki timekata kunci yang dapat digunakan untuk menentukan waktu pipa. Untuk menonaktifkan kata kunci itu sehingga timeperintah (dalam sistem file) digunakan, Anda dapat mengutipnya seperti "time" foo.sh. Lihat juga unix.stackexchange.com/search?q=user%3A22565+time+keyword
Stéphane Chazelas
6

Berikut ini solusinya:

  1. menghilangkan [s] waktu yang dibutuhkan untuk setiap shell untuk memuat dan menginisialisasi sendiri

  2. dapat dijalankan dari dalam setiap shell

  3. Penggunaan

    tidak ada timeperintah bawaan shell , karena tidak ada yang portabel atau umum

  4. Bekerja di semua shell yang kompatibel dengan POSIX.
  5. Bekerja pada semua sistem yang kompatibel dengan POSIX dan XSI dengan kompiler C , atau di mana Anda dapat mengkompilasi C yang dapat dieksekusi di muka.
  6. Menggunakan implementasi waktu yang sama pada semua shell.

Ada dua bagian: program C pendek yang membungkus gettimeofday, yang sudah usang tetapi masih lebih portabel daripada clock_gettime, dan skrip shell pendek yang menggunakan program itu untuk mendapatkan jam presisi mikrodetik membaca kedua sisi sumber skrip. Program C adalah satu-satunya cara portabel dan overhead minimal untuk mendapatkan ketepatan sub-detik pada timestamp.

Berikut adalah program C epoch.c:

#include <sys/time.h>
#include <stdio.h>
int main(int argc, char **argv) {
    struct timeval time;
    gettimeofday(&time, NULL);
    printf("%li.%06i", time.tv_sec, time.tv_usec);
}

Dan skrip shell timer:

#!/bin/echo Run this in the shell you want to test

START=$(./epoch)
. "$1"
END=$(./epoch)
echo "$END - $START" | bc

Ini adalah bahasa perintah shell standar dan bcdan harus berfungsi sebagai skrip di bawah setiap shell yang kompatibel dengan POSIX.

Anda dapat menggunakan ini sebagai:

$ bash timer ./test.sh
.002052
$ dash timer ./test.sh
.000895
$ zsh timer ./test.sh
.000662

Itu tidak mengukur waktu sistem atau pengguna, hanya jam dinding non-monoton yang berlalu. Jika jam sistem berubah selama eksekusi skrip, ini akan memberikan hasil yang salah. Jika sistem sedang dimuat, hasilnya akan tidak dapat diandalkan. Saya tidak berpikir sesuatu yang lebih baik bisa dibawa-bawa di antara cangkang.

evalSkrip pengatur waktu yang dimodifikasi dapat digunakan untuk menjalankan perintah di luar skrip.

Michael Homer
sumber
Secara kebetulan, tepat sebelum membaca ini, (dan baris terakhir tentang eval), saya mengutak - atik naskah dalam jawaban Rabin untuk dimasukkan eval "$@", sehingga bisa menjalankan builtin shell dengan cepat.
AGC
4

Beberapa kali solusi yang direvisi menggunakan /proc/uptimedan dc/ bc/ awksebagian besar berkat input oleh AGC :

#!/bin/sh

read -r before _ < /proc/uptime

sleep 2s # do something...

read -r after _ < /proc/uptime

duration=$(dc -e "${after} ${before} - n")
# Alternative using bc:
#   duration=$(echo "${after} - ${before}" | bc)
# Alternative using awk:
#   duration=$(echo "${after} ${before}" | awk '{print $1 - $2}')

echo "It took $duration seconds."

Berasumsi jelas bahwa /proc/uptimeada dan memiliki bentuk tertentu.

phk
sumber
3
Ini akan membuatnya portabel di antara shell, tetapi tidak portabel di antara implementasi Unix karena beberapa hanya kekurangan sistem /procfile. Jika ini masalah atau tidak, saya tidak tahu.
Kusalananda
1
Untuk penekanan, Q ini menyarankan kecepatan floppy disk atau lebih buruk. Dalam hal ini overhead pemuatan awkdapat menjadi signifikan. Mungkin b=$(cat /proc/uptime)sebelum, lalu a=$(cat /proc/uptime)setelah, lalu parsing $ a dan $ b dan kurangi.
AGC
@ Agc Input yang bagus, terima kasih, saya menambahkan beberapa solusi alternatif yang sesuai.
phk
1
Tidak memikirkan itu sebelumnya, tapi jika builtin seperti readlebih baik dari cat, ini akan menjadi bersih (dan sedikit lebih cepat): read before dummyvar < /proc/uptime ;sleep 2s;read after dummyvar < /proc/uptime; duration=$(dc -e "${after} ${before} - n");echo "It took $duration seconds."
AGC