ทางเลือกมาตรฐานแบบไม่ใช้เวลามาตรฐานสากล? [ปิด]


10

สำหรับการเปรียบเทียบครั้งเรียกใช้สคริปต์ระหว่างเปลือกหอยแตกต่างกันบางคำตอบ SE แนะนำให้ใช้bash's ในตัว timeคำสั่งเช่นดังนั้น:

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

... ฯลฯให้ทุกเชลล์ทำการทดสอบ มาตรฐานดังกล่าวล้มเหลวในการกำจัดเวลาที่แต่ละเปลือกโหลดและเริ่มต้นตัวเอง ตัวอย่างเช่นสมมติว่าทั้งสองคำสั่งดังกล่าวถูกจัดเก็บไว้ในอุปกรณ์ช้ากับความเร็วในการอ่านของฟล็อปปี้ดิสก์ต้น (124KB / s), dash(ก~ 150Kปฏิบัติการ) จะโหลดประมาณ7 เท่าเร็วกว่าbash( ~ 1M ) เปลือก เวลาในการโหลดจะเอียงtimeตัวเลข - เวลาก่อนการโหลดของกระสุนเหล่านั้นไม่เกี่ยวข้องกับการวัดเวลาการทำงานของfoo.shใต้แต่ละเชลล์หลังจากกระสุนถูกโหลดแล้ว

แบบพกพาและยูทิลิตี้ทั่วไปที่ดีที่สุดในการรันสำหรับกำหนดเวลาสคริปต์ที่สามารถเรียกใช้จากภายในแต่ละเชลล์คืออะไร ดังนั้นโค้ดด้านบนจะมีลักษณะดังนี้:

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

หมายเหตุ: ไม่มีคำสั่งในตัวของ เชลล์timeเนื่องจากไม่มีแบบพกพาหรือทั่วไป


ยังดีกว่าหากการใช้งานนั้นสามารถเปรียบเทียบเวลาที่ใช้โดยคำสั่งและท่อภายในของเชลล์โดยที่ผู้ใช้ไม่ต้องรวมคำสั่งเหล่านั้นไว้ในสคริปต์ ไวยากรณ์ประดิษฐ์เช่นนี้จะช่วย:

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

หอยบางตัวtimeสามารถจัดการสิ่งนี้ได้ ตัวอย่างbash -c "time while false ; do : ; done"งาน หากต้องการดูว่าอะไรทำงาน (และไม่) ในระบบของคุณลอง:

tail +2 /etc/shells | 
while read s ; do 
    echo $s ; $s -c "time while false ; do : ; done" ; echo ----
done

6
ใช้เพียงแค่/usr/bin/time?
Kusalananda

1
ฉันไม่เข้าใจวิธีการใด ๆที่ไม่ใช่ builtin อาจจะทั้ง "กำจัดเวลาที่แต่ละเปลือกโหลดและเริ่มต้นตัวเอง" และรันสคริปต์แบบสแตนด์อโลนในขณะที่เป็น "แบบพกพาและทั่วไป"
Michael Homer

1
นั่นไม่ใช่คำตอบสำหรับคำถาม แต่เป็นพรอมต์ให้คุณชี้แจงสิ่งที่คุณต้องการ
Michael Homer

1
ฉันโพสต์ความพยายามอย่างดีที่สุดแล้ว แต่ฉันคิดว่าคำถามยังไม่ได้รับการเน้นย้ำเกี่ยวกับสิ่งที่จริง ๆ แล้วพยายามจะบรรลุ
Michael Homer

2
“ พกพาหรือทั่วไป” หมายถึงอะไร เชลล์บิวด์อินเป็นแบบพกพา (ทำงานได้กับระบบหลายระบบ) และทั่วไปมากขึ้น (ทำงานในสถานการณ์มากขึ้นเนื่องจากสามารถกำหนดเวลาอย่างอื่นที่นอกเหนือจากการเรียกใช้ไฟล์) เป็นคำสั่งภายนอก คุณพยายามแก้ไขปัญหาอะไร
Gilles 'หยุดความชั่วร้าย'

คำตอบ:


10

คุณควรทราบว่าtimeมีการระบุโดย POSIXและ AFAICT ตัวเลือกเดียวที่ POSIX กล่าวถึง ( -p) ได้รับการสนับสนุนอย่างถูกต้องโดยเชลล์ต่างๆ:

$ 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

1
timeปัญหาคือว่าจะสามารถที่จะเปรียบเทียบการกำหนดเวลาผลที่จะต้องมีการหมดเวลาโดยการดำเนินงานเดียวกันของ มันเปรียบได้กับการให้นักวิ่งแข่งวัดเวลาของพวกเขาเองในระยะ 100 เมตรแยกกันแทนที่จะทำด้วยนาฬิกาเดียวในเวลาเดียวกัน เห็นได้ชัดว่าเป็น nitpicking แต่ยัง ...
Kusalananda

@ Kusalananda ฉันคิดว่าปัญหาคือ OP คิดว่าtimeไม่สามารถพกพาได้ ดูเหมือนว่าจะพกพาได้ (ผมเห็นด้วยกับจุดของคุณในการเปรียบเทียบแม้ว่า)
Muru

@muru บนระบบของฉันdash -c 'time -p while false ; do : ; done'คืนค่า"time: ไม่สามารถรันในขณะที่: ไม่มีไฟล์หรือไดเรกทอรีเช่น <cr> คำสั่งที่ออกโดยไม่มีสถานะเป็นศูนย์ 127"ข้อผิดพลาด
agc

1
@agc POSIX ยังกล่าวว่า: "คำว่ายูทิลิตี้ที่ใช้แทนคำสั่งเพื่อเน้นความจริงที่ว่าคำสั่งผสมสารประกอบ, ท่อ, built-in พิเศษและอื่น ๆ ไม่สามารถใช้โดยตรงอย่างไรก็ตามยูทิลิตี้รวมถึงโปรแกรมประยุกต์ของผู้ใช้และ เชลล์สคริปต์ไม่เพียง แต่เป็นยูทิลิตี้มาตรฐาน " (ดูหัวข้อ RATIONALE)
muru


7

ฉันใช้คำสั่งGNU dateซึ่งรองรับตัวจับเวลาความละเอียดสูง:

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

"$@" &> /dev/null

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

แล้วฉันจะเรียกสคริปต์เช่นนี้

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

หน่วยเอาต์พุตมีหน่วยเป็นมิลลิวินาที


1
สมมติว่าเวลา (เวลายุค) ไม่เปลี่ยนแปลงในระหว่าง ไม่สามารถนึกถึงกรณีในทางปฏิบัติที่มันจะทำให้เกิดปัญหา แต่ก็ยังมีมูลค่าการกล่าวขวัญ
phk

1
คุณควรเพิ่มว่านี่ต้องใช้ GNU dateโดยเฉพาะ
Kusalananda

@phk โปรดอธิบาย?
ราบิน

1
@Rabin สมมติว่าไคลเอนต์ NTP ของคุณมีปัญหาและอัปเดตและเปลี่ยนนาฬิกาของคุณระหว่างที่STARTและENDตั้งอยู่จากนั้นสิ่งนี้จะส่งผลต่อผลลัพธ์ของคุณอย่างเห็นได้ชัด ไม่มีความคิดว่าคุณต้องการมันอย่างแม่นยำและสำคัญอย่างไรในกรณีของคุณ แต่อย่างที่ฉันพูดสิ่งที่คุณควรคำนึงถึง (เรื่องสนุก: ฉันรู้ซอฟต์แวร์ที่นำไปสู่ผลลัพธ์เชิงลบโดยไม่คาดคิด - มันถูกใช้เพื่อการคำนวณปริมาณงาน - ซึ่งทำลายบางสิ่ง)
phk

1
นอกจากนี้ไคลเอนต์ NTP บางส่วนจะไม่ชะลอตัวลงและเพิ่มความเร็วนาฬิกาแทนที่จะทำให้ "ข้าม" ในเวลาระบบหรือไม่ หากคุณมีลูกค้า NTP เช่นนั้นและคุณได้กำหนดเวลาเย็นเมื่อวานนี้พวกเขาอาจถูกบิดเบือนโดยลูกค้า NTP "คาดการณ์" การก้าวกระโดดครั้งที่สอง (หรือนาฬิกาของระบบทำงานเพียง 61 ครั้งในกรณีนั้น?)
Jörg W Mittag

6

timeยูทิลิตี้มักจะถูกสร้างขึ้นในเปลือกที่เป็นคุณได้สังเกตเห็นซึ่งจะทำให้มันไร้ประโยชน์เป็น "เป็นกลาง" จับเวลา

อย่างไรก็ตามยูทิลิตีนี้มักจะมีให้ใช้เป็นยูทิลิตีภายนอก/usr/bin/timeซึ่งอาจใช้เพื่อทำการทดสอบเวลาที่คุณเสนอ

$ bash -c '/usr/bin/time foo.sh'

สิ่งนี้จะ "กำจัดเวลาที่ใช้สำหรับแต่ละเชลล์ในการโหลดและเริ่มต้นตัวเอง" ได้อย่างไร?
Michael Homer

1
หากfoo.shสามารถเรียกใช้งานได้และมี shebang นี่จะทำงานด้วยเชลล์เดียวกันเสมอและจะนับเวลาเริ่มต้นของเชลล์นั้นดังนั้นนี่ไม่ใช่สิ่งที่ OP ต้องการ หากfoo.shหายไปหนึ่งในนั้นก็ไม่ได้เลย
Kevin

@ เควินจริงมาก ฉันแค่นำ "ไม่มีกระสุนในตัวtime" มาพิจารณาเท่านั้นดูเหมือนว่า เวลาเริ่มต้นเชลล์อาจต้องแยกกัน
Kusalananda

1
ฉันไม่รู้จักเชลล์ที่มีtimeคำสั่ง builtin อย่างไรก็ตามเชลล์จำนวนมากรวมถึงbashมีtimeคีย์เวิร์ดที่สามารถใช้กับไพพ์ไลน์ไทม์ไลน์ เพื่อปิดการใช้คำหลักนั้นเพื่อให้timeคำสั่ง (ในระบบแฟ้ม) "time" foo.shถูกนำมาใช้คุณสามารถพูดมันเหมือน ดูเพิ่มเติมunix.stackexchange.com/search?q=user%3A22565+time+keyword
Stéphane Chazelas

6

นี่คือทางออกที่:

  1. กำจัด [s] เวลาที่แต่ละเปลือกโหลดและเริ่มต้นเอง

  2. สามารถเรียกใช้จากภายในแต่ละเชลล์

  3. การใช้ประโยชน์

    ไม่มีtimeคำสั่งในตัวของเชลล์เนื่องจากไม่มีแบบพกพาหรือทั่วไป

  4. ใช้ได้กับเชลล์ที่ใช้ POSIX ได้ทั้งหมด
  5. ทำงานบนระบบที่รองรับ POSIX และ XSI ได้ทั้งหมดด้วยคอมไพเลอร์ Cหรือที่คุณสามารถคอมไพล์ไฟล์สั่งการ C ล่วงหน้าได้
  6. ใช้การปรับใช้เวลาแบบเดียวกันบนทุกเชลล์

มีสองส่วนคือโปรแกรม C แบบสั้นที่แรพgettimeofdayซึ่งเลิกใช้แล้ว แต่ยังพกพาได้มากกว่าclock_gettimeและสคริปต์เชลล์สั้นที่ใช้โปรแกรมนั้นเพื่อรับนาฬิกาที่มีความแม่นยำระดับไมโครวินาทีอ่านทั้งสองด้านของการจัดหาสคริปต์ โปรแกรม C เป็นวิธีพกพาและค่าใช้จ่ายน้อยที่สุดในการรับความแม่นยำย่อยวินาทีในการประทับเวลา

นี่คือโปรแกรม 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);
}

และเชลล์สคริปต์timer:

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

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

นี่คือมาตรฐานภาษาคำสั่งของเชลล์และbcและควรจะทำงานเป็นสคริปต์ภายใต้เปลือก POSIX ได้ใด ๆ

คุณสามารถใช้สิ่งนี้เป็น:

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

มันไม่ได้วัดเวลาของระบบหรือผู้ใช้เวลาที่ผ่านไปของนาฬิกาแขวนผนังที่ไม่ใช่แบบ monotonic หากนาฬิกาของระบบมีการเปลี่ยนแปลงระหว่างการทำงานของสคริปต์สิ่งนี้จะให้ผลลัพธ์ที่ไม่ถูกต้อง หากระบบอยู่ระหว่างการโหลดผลลัพธ์จะไม่น่าเชื่อถือ ฉันไม่คิดว่าอะไรที่ดีกว่าสามารถพกพาได้ระหว่างเปลือกหอย

สคริปต์ตัวจับเวลาที่ถูกปรับเปลี่ยนสามารถใช้evalแทนการเรียกใช้คำสั่งภายนอกสคริปต์


ก่อนที่จะอ่านเรื่องนี้ (และบรรทัดสุดท้ายเกี่ยวกับเรื่องนี้eval) ฉันได้ทวีตสคริปต์ในคำตอบของRabinเพื่อรวมeval "$@"ไว้ดังนั้นมันจึงสามารถรันเชลล์บิวด์อินได้ทันที
agc

4

โซลูชันที่แก้ไขหลายครั้งโดยใช้/proc/uptimeและdc/ bc/ awkในชิ้นส่วนขนาดใหญ่ต้องขอบคุณ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."

สมมติว่า/proc/uptimeมีอยู่และมีรูปแบบที่แน่นอน


3
สิ่งนี้จะทำให้มันพกพาได้ระหว่างเชลล์ แต่ไม่สามารถพกพาได้ระหว่าง Unix implementations เนื่องจากบางตัวขาด/procระบบไฟล์ หากนี่เป็นข้อกังวลหรือไม่ฉันไม่รู้
Kusalananda

1
สำหรับการเน้นคำถามนี้ขอแนะนำความเร็วฟลอปปี้ดิสก์หรือแย่กว่านั้น ในกรณีนี้ค่าใช้จ่ายในการโหลดawkอาจมีความสำคัญ อาจจะเป็นb=$(cat /proc/uptime)ก่อนหน้านี้a=$(cat /proc/uptime)หลังจากนั้นจึงแยกวิเคราะห์$ aและ$ bแล้วลบออก
agc

@agc ข้อมูลที่ดีขอบคุณฉันได้เพิ่มทางเลือกอื่น ๆ ตามลำดับ
phk

1
ไม่ได้คิดมาก่อน แต่ถ้าbuiltinsชอบreadจะดีกว่าcatนี้จะทำความสะอาด (และเร็วขึ้นเล็กน้อย): read before dummyvar < /proc/uptime ;sleep 2s;read after dummyvar < /proc/uptime; duration=$(dc -e "${after} ${before} - n");echo "It took $duration seconds."
AGC
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.