प्रोग्राम निष्पादन के समय को कैसे मापें और एक चर के अंदर स्टोर करें


61

यह जानने के लिए कि किसी बैश (v4 +) स्क्रिप्ट के भीतर कितने लंबे ऑपरेशन हुए हैं, मैं आउटपुट को time"अलग से" कमांड से पार्स करना चाहूंगा और (अंततः) इसे बैश वेरिएबल ( let VARNAME=...) के भीतर कैप्चर करूंगा ।

अब, मैं बैश बिल्ट-इन के कारण time -f '%e' ...(या बल्कि) का उपयोग कर रहा हूं command time -f '%e' ..., लेकिन चूंकि मैं पहले से ही निष्पादित कमांड के आउटपुट को रीडायरेक्ट कर रहा हूं, इसलिए मैं वास्तव में खो गया हूं कि मैं timeकमांड के आउटपुट को कैप्चर करने के लिए कैसे जाऊंगा । असल में समस्या यहाँ है उत्पादन को अलग करने की timeनिष्पादित आदेश (रों) के उत्पादन से।

मैं जो चाहता हूं वह कमांड शुरू करने और उसके पूरा होने के बीच सेकंड (पूर्णांक) में समय की मात्रा की गणना करने की कार्यक्षमता है। यह timeकमांड या संबंधित बिल्ट-इन होना जरूरी नहीं है ।


संपादित करें: नीचे दिए गए दो उपयोगी उत्तर, मैं दो स्पष्टीकरण जोड़ना चाहता था।

  1. मैं निष्पादित कमांड के आउटपुट को फेंकना नहीं चाहता, लेकिन यह वास्तव में कोई फर्क नहीं पड़ेगा कि यह स्टडआउट या हैडर पर समाप्त होता है या नहीं।
  2. मैं एक अप्रत्यक्ष एक पर एक प्रत्यक्ष दृष्टिकोण (यानी उत्पादन को सीधे मध्यवर्ती फाइलों में संग्रहीत करने के लिए विरोध के रूप में पकड़ता हूं) पर पसंद करूंगा।

dateअब तक का उपयोग करने के लिए समाधान मैं क्या चाहता है बंद हो जाता है।


डेटा प्राप्त करने का सबसे सीधा तरीका है और इसे संभालते हुए इसे अभी भी सामान्य रूप से चलने देना सी प्रोग्राम का उपयोग करके fork(), execvp()और wait3()/wait4()। यह आखिरकार समय और दोस्त क्या कर रहे हैं। मैं एक फ़ाइल या समान दृष्टिकोण पर पुनर्निर्देशन के बिना इसे bash / perl में करने के लिए एक नकली तरीके से अवगत नहीं हूं।
पेंग्विन 359

एक संबंधित प्रश्न यह है कि आपको यहां दिलचस्प लग सकता है
कालेब

@ कालेब: धन्यवाद। वास्तव में दिलचस्प है। हालाँकि, मेरे उद्देश्यों के लिए यह एक स्क्रिप्ट में करने के लिए पर्याप्त है।
0xC0000022L

जवाबों:


85

timeएक var का आउटपुट प्राप्त करने के लिए निम्नलिखित का उपयोग करें:

usr@srv $ mytime="$(time ( ls ) 2>&1 1>/dev/null )"
usr@srv $ echo "$mytime"

real    0m0.006s
user    0m0.001s
sys     0m0.005s

आप केवल एकल समय प्रकार के लिए भी पूछ सकते हैं, उदाहरण के लिए:

usr@srv $ utime="$( TIMEFORMAT='%lU';time ( ls ) 2>&1 1>/dev/null )"
usr@srv $ echo "$utime"
0m0.000s

उस समय को प्राप्त करने के लिए जिसका आप उपयोग कर सकते हैं date +%s.%N, इसलिए निष्पादन से पहले और बाद में इसे लें और अंतर की गणना करें:

START=$(date +%s.%N)
command
END=$(date +%s.%N)
DIFF=$(echo "$END - $START" | bc)
# echo $DIFF

4
मैं हालांकि आउटपुट को कमांड से दूर नहीं फेंकना चाहता था। इसलिए मुझे लगता है कि आपका तीसरा कोड ब्लॉक मेरे दिमाग में सबसे करीब है। यद्यपि मैं अंतिम एक के रूप में लिखूंगा DIFF=$((END-START)), अंकगणितीय अभिव्यक्तियों का उपयोग करना। :) ... जवाब के लिए धन्यवाद। +1
0xC0000022L

1
@STATUS_ACCESS_DENIED: बैश फ़्लोटिंग पॉइंट अंकगणित नहीं करता है, इसलिए यदि आप दूसरे रिज़ॉल्यूशन ( .%Nद्विपक्ष के कोड में) से बेहतर चाहते हैं , तो आपको या तो bcगणना की जरूरत है या कट्टरपंथी गणनाओं की।
गाइल्स

@ गिल्स: मुझे पता है। जैसा कि मैंने अपने प्रश्न में लिखा था कि पूर्णांक ठीक हैं। दूसरे से उच्च संकल्प की आवश्यकता नहीं है। लेकिन धन्यवाद, तारीख के आह्वान को बदलना होगा। मुझे एहसास हुआ था, हालांकि।
0xC0000022L

6
FYI करें, दिनांक स्वरूपण N, Mac OS X पर काम नहीं करता है, यह सिर्फ "N" देता है। उबंटू पर ठीक है।
डेविड

ध्यान दें कि time (cmd) 2> somethingटाइमिंग आउटपुट को रीडायरेक्ट करने के दौरान file, यह (प्रलेखन के अनुसार) के लिए नहीं है, अन्य शेल में नहीं है जहां timeएक कीवर्ड है और इसे बग के रूप में माना जा सकता है । मैं इस पर भरोसा नहीं करेगा क्योंकि यह भविष्य के संस्करणों में काम नहीं कर सकता है bash
स्टीफन चेजलस

17

बैश में, timeनिर्माण का आउटपुट इसकी मानक त्रुटि पर जाता है, और आप इसे प्रभावित पाइपलाइन की मानक त्रुटि को पुनर्निर्देशित कर सकते हैं। तो चलिए एक कमांड से शुरू करते हैं जो अपने आउटपुट और एरर स्ट्रीमस को लिखता है sh -c 'echo out; echo 1>&2 err':। आउटपुट से कमांड की एरर स्ट्रीम को मिक्स नहीं करने के लिए time, हम अस्थायी रूप से कमांड की एरर स्ट्रीम को अलग फाइल डिस्क्रिप्टर में डायवर्ट कर सकते हैं:

{ time -p sh -c 'echo out; echo 1>&2 err' 2>&3; }

यह outएफडी 1 को लिखता है, errएफडी 3 को, और एफडी 2 को समय:

{ time -p sh -c 'echo out; echo 1>&2 err' 2>&3; } \
    3> >(sed 's/^/ERR:/') 2> >(sed 's/^/TIME:/') > >(sed 's/^/OUT:/')

यह errfd 2 और fd 3 पर आने वाले समय के लिए अधिक सुखद होगा , इसलिए हम उन्हें स्वैप करते हैं, जो कि बोझिल है क्योंकि दो फ़ाइल विवरणों को स्वैप करने का कोई सीधा तरीका नहीं है:

{ { { time -p sh -c 'echo out; echo 1>&2 err' 2>&3; } 3>&2 2>&4; } 4>&3; } 3> >(sed 's/^/TIME:/') 2> >(sed 's/^/ERR:/') > >(sed 's/^/OUT:/')

यह दिखाता है कि आप कमांड के आउटपुट को कैसे बढ़ा सकते हैं, लेकिन यदि आप कमांड के आउटपुट और उसके समय दोनों को कैप्चर करना चाहते हैं, तो आपको अधिक मेहनत करने की आवश्यकता है। एक अस्थायी फ़ाइल का उपयोग करना एक समाधान है। वास्तव में, यह एकमात्र विश्वसनीय समाधान है यदि आपको कमांड की मानक त्रुटि और इसके मानक आउटपुट दोनों को कैप्चर करने की आवश्यकता है। लेकिन अन्यथा, आप पूरे आउटपुट पर कब्जा कर सकते हैं और इस तथ्य का लाभ उठा सकते हैं कि timeएक पूर्वानुमेय प्रारूप है (यदि आप POSIX प्रारूप या बैश-विशिष्ट चर time -pप्राप्त करने के लिए उपयोग करते हैं )।TIMEFORMAT

nl=$'\n'
output=$(TIMEFORMAT='%R %U %S %P'; mycommand)
set ${output##*$nl}; real_time=$1 user_time=$2 system_time=$3 cpu_percent=$4
output=${output%$nl*}

यदि आप केवल दीवार घड़ी के समय के बारे में परवाह करते हैं, तो dateपहले और बाद में चलने वाला एक सरल समाधान है (यदि अतिरिक्त समय के कारण बाहरी कमांड को लोड करने में थोड़ा अधिक प्रभाव पड़ता है)।


वाह, बहुत परीक्षण करने के लिए। व्यापक उत्तर के लिए बहुत बहुत धन्यवाद। +1।
0xC0000022L

7

समय के साथ, कमांड आउटपुट स्टैडआउट पर निकलता है और समय स्टडर पर निकलता है। इसलिए, उन्हें अलग करने के लिए, आप कर सकते हैं:

command time -f '%e' [command] 1>[command output file] 2>[time output file]

लेकिन, अब समय एक फाइल में है। मुझे नहीं लगता कि बैश सीधे वेरिएबल में स्टैडर लगाने में सक्षम है। यदि आप कमांड के आउटपुट को कहीं पर पुनर्निर्देशित करने से गुरेज नहीं करते हैं, तो आप कर सकते हैं:

FOO=$((( command time -f '%e' [command]; ) 1>outputfile; ) 2>&1; )

जब आप ऐसा करते हैं, तो कमांड का आउटपुट अंदर होगा outputfileऔर इसे चलाने में लगने वाला समय अंदर होगा $FOO


1
ओह, मुझे एहसास नहीं हुआ। मैं जानता था कि timeस्टादर को लिखता हूं , लेकिन मुझे नहीं पता था कि यह स्टैडआउट और निष्पादित कमांड के स्टेटर को उसके स्टडआउट में मिला देगा। लेकिन आम तौर पर कोई प्रत्यक्ष विधि नहीं है (यानी मध्यवर्ती फ़ाइल के बिना)? +1।
19x पर 0xC0000022L

3
@STATUS_ACCESS_DENIED: timeइसकी कमांड stdoutऔर में विलय नहीं करता है stderr। जिस तरह से मैंने दिखाया कि आपको केवल कमांड के स्टडआउट आउटपुट की आवश्यकता थी। चूंकि bashकेवल वही स्टोर होगा जो बाहर आता है stdout, आपको पुनर्निर्देशित करना होगा। मामले में आप सुरक्षित रूप से अपने कमांड के डंठल को छोड़ सकते हैं: समय हमेशा स्टाडर की अंतिम पंक्ति पर रहेगा। यदि आपको अपने कमांड के आउटपुट स्ट्रीम दोनों की आवश्यकता है, तो मैं इसे अन्य स्क्रिप्ट में लपेटने का सुझाव दूंगा।
मेरीस

6

यदि आप bash(और नहीं sh) हैं और आपको उप-सेकंड सटीकता की आवश्यकता नहीं है, तो आप कॉल को dateपूरी तरह से छोड़ सकते हैं और इसे बिना किसी अतिरिक्त प्रक्रिया के, संयुक्त आउटपुट को अलग किए बिना, और आउटपुट को कैप्चर और पार्स किए बिना कर सकते हैं। किसी भी आदेश से:

# SECONDS is a bash special variable that returns the seconds since set.
SECONDS=0
mycmd <infile >outfile 2>errfile
DURATION_IN_SECONDS=$SECONDS
# Now `$DURATION_IN_SECONDS` is the number of seconds.

अच्छा लगा। मैंने पहले कभी SECONDS के बारे में नहीं सुना
ब्रूनो 9779

क्या आपको पता है कि किस बैश संस्करण के साथ शुरुआत हुई थी?
0xC0000022L

4

उस उद्देश्य के लिए इसका उपयोग करना times( शायद) बेहतर है time, bashजिसमें शेल के लिए संचित उपयोगकर्ता और सिस्टम बार प्रिंट करता है और शेल से चलने वाली प्रक्रियाओं के लिए, उदाहरण का उपयोग करें:

$ (sleep 2; times) | (read tuser tsys; echo $tuser:$tsys)
0m0.001s:0m0.003s

देखें: help -m timesअधिक जानकारी के लिए


4

पिछले सभी प्रतिक्रियाओं को एक साथ रखना, जब OSX में

ProductName:    Mac OS X
ProductVersion: 10.11.6
BuildVersion:   15G31+

आप पसंद कर सकते हैं

microtime() {
    python -c 'import time; print time.time()'
}
compute() {
    local START=$(microtime)
    #$1 is command $2 are args
    local END=$(microtime)
    DIFF=$(echo "$END - $START" | bc)
    echo "$1\t$2\t$DIFF"
}

1

स्थापित /bin/time(जैसे pacman -S time)

-fध्वज को आज़माते समय त्रुटि के बजाय :

$ time -f %e sleep 0.5
bash: -f: command not found

real    0m0.001s
user    0m0.001s
sys     0m0.001s

आप वास्तव में इसका उपयोग कर सकते हैं:

$ /bin/time -f %e sleep 0.5
0.50

और जो आप चाहते हैं वह प्राप्त करें - चर में समय (उदाहरण %eवास्तविक समय के लिए उपयोग करता है , अन्य विकल्पों की जांच के लिए man time):

#!/bin/bash
tmpf="$(mktemp)"
/bin/time -f %e -o "$tmpf" sleep 0.5
variable="$(cat "$tmpf")"
rm "$tmpf"

echo "$variable"

/usr/bin/timeकई प्रणालियों पर
बेसिल स्टायरनेविच 11

अगर आप बारीकी से देखें तो मैंने बताया कि मेरे सवाल में। timeबैश (और संभवतः अन्य शेल) में एक शेल बिल्टिन है, लेकिन जब तक timeनिष्पादन योग्य के लिए रास्ता अंदर है PATH, हम command timeयह सुनिश्चित करने के लिए उपयोग कर सकते हैं कि हम बाहरी कमांड को रनिंग के विपरीत बना रहे हैं।
0xC0000022L

1

उपरोक्त कथनों के साथ काम करते समय और विशेष रूप से ग्रेज़गोरज़ के उत्तर को ध्यान में रखते हुए एक सिर के रूप में। मैं अपने Ubuntu 16.04 Xenial सिस्टम को इन दो परिणामों को देखकर आश्चर्यचकित था:

$ which time
/usr/bin/time

$ time -f %e sleep 4
-f: command not found
real    0m0.071s
user    0m0.056s
sys     0m0.012s

$ /usr/bin/time -f %e sleep 4
4.00

मेरे पास कोई भी उपनाम नहीं है और इसलिए मुझे नहीं पता कि ऐसा क्यों हो रहा है।


1
timeएक बिलियन शेल भी है।
बेसिल स्टारीनेवविच

यदि आप यह सुनिश्चित करना चाहते हैं कि आप कमांड चला रहे हैं command, तो उपयोग करें , यदि आप यह सुनिश्चित करना चाहते हैं कि आप बिलिन चला रहे हैं, तो उपयोग करें builtin। यहां कोई जादू नहीं है। का प्रयोग करें helpउपलब्ध आदेशों यह पता लगाने की या type <command>यह पता लगाने की किस प्रकार <command>है (उदाहरण के लिए एक builtin, बाहरी कमांड, समारोह या अन्य नाम)।
0xC0000022L

बेसिकली, आप काफी सही हैं। मैंने commandकमांड के महत्व को नहीं देखा था । जो तथ्य यह सुनिश्चित करता है कि / usr / bin / time कहा जाता है। इसका अर्थ है कि निम्नलिखित दो कथन संगत हैं: $ command time -f %e sleep 4और$ /usr/bin/time -f %e sleep
पॉल प्रिचार्ड

0

इसे आज़माएं, यह तर्कों के साथ एक सरल कमांड चलाता है और कई बार $ $ उपयोगकर्ता $ sys डालता है और निकास कोड को संरक्षित करता है।

यह वास्तविक उपयोगकर्ता sys को छोड़कर किसी भी चर पर सदस्यता या ट्रॉम्प को कांटा नहीं करता है, और अन्यथा स्क्रिप्ट के चलने में हस्तक्षेप नहीं करता है

timer () {
  { time { "$@" ; } 2>${_} {_}>&- ; } {_}>&2 2>"/tmp/$$.$BASHPID.${#FUNCNAME[@]}"
  set -- $?
  read -d "" _ real _ user _ sys _ < "/tmp/$$.$BASHPID.${#FUNCNAME[@]}"
  rm -f "/tmp/$$.$BASHPID.${#FUNCNAME[@]}"
  return $1
}

जैसे

  timer find /bin /sbin /usr rm /tmp/
  echo $real $user $sys

ध्यान दें: यह केवल सरल कमांड है जो पाइपलाइन नहीं है, जिसके घटक एक उपधारा में चलते हैं

यह संस्करण आपको $ 1 के रूप में निर्दिष्ट करने की अनुमति देता है चर का नाम जिसे 3 बार प्राप्त करना चाहिए:

timer () {
  { time { "${@:4}" ; } 2>${_} {_}>&- ; } {_}>&2 2>"/tmp/$$.$BASHPID.${#FUNCNAME[@]}"
  set -- $? "$@"
  read -d "" _ "$2" _ "$3" _ "$4" _ < "/tmp/$$.$BASHPID.${#FUNCNAME[@]}"
  rm -f "/tmp/$$.$BASHPID.${#FUNCNAME[@]}"
  return $1
}

जैसे

  timer r u s find /bin /sbin /usr rm /tmp/
  echo $r $u $s

और उपयोगी हो सकता है अगर यह समय पर रौंदने से बचने के लिए पुनरावर्ती कहा जा रहा है; लेकिन तब रस आदि को उनके उपयोग में स्थानीय घोषित किया जाना चाहिए।

http://blog.sam.liddicott.com/2016/01/timeing-bash-commands.html

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.