Làm thế nào để có được thời gian thực hiện của một kịch bản hiệu quả?


340

Tôi muốn hiển thị thời gian hoàn thành của một kịch bản.

Những gì tôi hiện đang làm là -

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

Đây chỉ là thời gian bắt đầu và kết thúc của kịch bản. Có thể hiển thị đầu ra hạt mịn như thời gian xử lý / thời gian io, v.v.?



Tôi nghĩ rằng câu trả lời được chấp nhận hiện tại là không thực sự đủ vì biến động thời gian.
Léo Léopold Hertz

1
@CiroSantilli 2016 六四 事件 Tôi nghĩ rằng đề xuất của bạn vẫn chưa đủ để loại bỏ ảnh hưởng của các sự kiện tạm thời.
Léo Léopold Hertz


Có một lệnh mà bạn có thể chạy và sau đó nó tự động lặp lại tất cả các lệnh unix, quên cách bật?
bột366

Câu trả lời:


449

Chỉ sử dụng timekhi bạn gọi tập lệnh:

time yourscript.sh

61
Đây quả đầu ra ba lần: real, usersys. Đối với ý nghĩa của những điều này, xem ở đây .
Garrett

27
và "thực" có lẽ là những gì mọi người muốn biết - "Real là đồng hồ treo tường - thời gian từ lúc bắt đầu đến khi kết thúc cuộc gọi"
Brad

3
Có cách nào để bắt thiết bị xuất chuẩn vào một tập tin không? Ví dụ, một cái gì đó nhưtime $( ... ) >> out.txt
Jon

1
Bạn cũng có thể thực hiện việc này theo chuỗi các lệnh trên dòng lệnh, ví dụ:time (command1 && command2)
meetar

1
Hoặc anh ta có thể, trong kịch bản của mình, chỉ cần làm: echo $ SECONDS giả sử đó là bash ....
Crossfit_and_Beer

170

Nếu timekhông phải là một lựa chọn,

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

runtime=$((end-start))

30
Lưu ý rằng điều này chỉ hoạt động nếu bạn không cần độ chính xác dưới giây. Đối với một số sử dụng có thể được chấp nhận, đối với những người khác thì không. Cho độ chính xác tốt hơn một chút (bạn vẫn gọi datehai lần, ví dụ, vì vậy bạn có thể ở tốt nhất có được millisecond chính xác trong thực tế, và có lẽ ít hơn), hãy thử sử dụng date +%s.%N. ( %Nlà nano giây kể từ toàn bộ giây.)
CVn

Điểm tốt. Tôi nghĩ về điều đó ngay sau khi rời khỏi bàn phím nhưng không quay lại. ^^ Cũng nên nhớ, OP, "ngày" đó sẽ tự thêm vài mili giây vào thời gian chạy.
Rob Bos

2
@ChrisH ơi. Tốt chỉ ra nó; bash mở rộng số học là chỉ số nguyên. Tôi thấy hai lựa chọn rõ ràng; hoặc bỏ khoảng thời gian trong chuỗi định dạng ngày (và coi giá trị kết quả là nano giây kể từ epoch), vì vậy hãy sử dụng date +%s%Nhoặc sử dụng thứ gì đó có khả năng hơn bcđể tính thời gian chạy thực tế từ hai giá trị như jwchew gợi ý. Tuy nhiên, tôi cảm thấy cách tiếp cận này là một cách làm tối ưu; timelà tốt hơn đáng kể nếu có sẵn, vì những lý do đã nêu ở trên.
một CVN

10
Chỉ cần cài đặt bcvà sau đó thực hiện việc này:runtime=$( echo "$end - $start" | bc -l )
redolent

10
Nếu tập lệnh của bạn mất vài phút, hãy sử dụng:echo "Duration: $((($(date +%s)-$start)/60)) minutes
rubo77

75

Chỉ cần gọi timesmà không có đối số khi thoát khỏi kịch bản của bạn.

Với kshhoặc zsh, bạn cũng có thể sử dụng timethay thế. Với zsh, timecũng sẽ cung cấp cho bạn thời gian đồng hồ treo tường ngoài thời gian CPU của người dùnghệ thống .

Để duy trì trạng thái thoát của tập lệnh của bạn, bạn có thể tạo nó:

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

Hoặc bạn cũng có thể thêm một cái bẫy vào EXIT:

trap times EXIT

Bằng cách đó, thời gian sẽ được gọi bất cứ khi nào vỏ thoát ra và trạng thái thoát sẽ được giữ nguyên.

$ 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

Cũng lưu ý rằng tất cả bash, kshzshcó một $SECONDSbiến đặc biệt tự động được tăng lên mỗi giây. Trong cả hai zshksh93, biến đó cũng có thể được tạo dấu phẩy động (với typeset -F SECONDS) để có độ chính xác cao hơn. Đây chỉ là thời gian đồng hồ treo tường, không phải thời gian CPU.


12
Biến $ SECONDS đó rất hữu ích, cảm ơn!
andybuckley

Cách tiếp cận của bạn loại bỏ ảnh hưởng của các sự kiện tạm thời? - - Tôi nghĩ rằng cách tiếp cận của bạn là gần timecách tiếp cận được trình bày trước đó. - - Tôi nghĩ rằng timeitcách tiếp cận được trình bày trong mã MATLAB có thể hữu ích ở đây.
Léo Léopold Hertz

2
Xin chào, @ Stéphane Chazelas. Tôi tìm thấy timeskhông cho thời gian tường, phải không? ví dụ:bash -c 'trap times EXIT;sleep 2'
user15964


32

Tôi hơi muộn với bandwagon, nhưng muốn đăng giải pháp của tôi (cho độ chính xác dưới giây) trong trường hợp những người khác tình cờ vấp phải chủ đề này thông qua tìm kiếm. Đầu ra có định dạng ngày, giờ, phút và cuối cùng là giây:

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

Hy vọng ai đó ngoài kia tìm thấy điều này hữu ích!


1
Tôi đoán không có cách nào khác (chỉ bash) ngoại trừ sử dụng 'bc' để thực hiện các phép tính. Kịch bản thực sự tốt của BTW;)
tvl

1
Xin lưu ý rằng FreeBSD datekhông hỗ trợ độ chính xác dưới giây và sẽ chỉ nối thêm chữ Nkhắc vào dấu thời gian.
Anton Samsonov

1
Kịch bản rất hay, nhưng bash của tôi không xử lý phần thứ hai. Tôi cũng đã học được rằng, 1 trong bc loại bỏ hiệu quả điều đó, vì vậy tôi đã thêm @ / 1 @ vào phép tính $ ds và bây giờ nó hiển thị công cụ rất hay!
Daniel

1
bc hỗ trợ modulo : dt2=$(echo "$dt%86400" | bc). Chỉ cần nói ... BTW Tôi thích hình thức dt2=$(bc <<< "${dt}%86400")nhưng đó hoàn toàn là cá nhân.
Dani_l

16

Phương pháp của tôi cho bash:

# Reset BASH time counter
SECONDS=0
    # 
    # do stuff
    # 
ELAPSED="Elapsed: $(($SECONDS / 3600))hrs $((($SECONDS / 60) % 60))min $(($SECONDS % 60))sec"

Điều này không hiển thị thời gian đã trôi qua, nhưng nó không hiển thị "đầu ra hạt mịn như thời gian xử lý, thời gian I / O" như được yêu cầu trong câu hỏi.
roaima

3
Những người tìm kiếm câu trả lời cho câu hỏi được đặt ra có khả năng tìm thấy giải pháp này hữu ích. Nhận xét của bạn sẽ được giải quyết tốt hơn cho câu hỏi OP.
Đánh dấu

5
#!/bin/bash
start=$(date +%s.%N)

# HERE BE CODE

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

echo "Runtime was $runtime"

Vâng, điều này gọi Python, nhưng nếu bạn có thể sống với điều đó thì đây là một giải pháp tuyệt vời, tuyệt vời.


5
Coi chừng việc chạy pythonlệnh đó trong một mạng con và đọc đầu ra của nó sẽ mất vài triệu nano giây trên hầu hết các hệ thống hiện tại. Tương tự cho việc chạy date.
Stéphane Chazelas

7
"Vài triệu nano giây" là vài mili giây. Các tập lệnh bash thời gian của mọi người thường không quan tâm lắm về điều đó (trừ khi họ chạy vài tỷ tập lệnh mỗi megasecond).
Zilk

2
Cũng lưu ý rằng, ngay cả khi tính toán được thực hiện trong một quy trình python, các giá trị đã được thu thập hoàn toàn trước khi python được gọi. Thời gian thực hiện của tập lệnh sẽ được đo chính xác, không có chi phí "hàng triệu nano giây".
Victor Schröder

5

Câu hỏi này khá cũ nhưng khi cố gắng tìm ra cách làm yêu thích của tôi, chủ đề này đã tăng lên ... và tôi ngạc nhiên không ai nhắc đến nó:

perf stat -r 10 -B sleep 1

'perf' là một công cụ phân tích hiệu suất được bao gồm trong kernel trong 'tools / perf' và thường có sẵn để cài đặt dưới dạng một gói riêng ('perf' trong CentOS và 'linux-tools' trên Debian / Ubuntu). Wiki Kernal perf Wiki có nhiều thông tin hơn về nó.

Chạy 'perf stat' cung cấp khá nhiều chi tiết bao gồm thời gian thực hiện trung bình ngay khi kết thúc:

1.002248382 seconds time elapsed                   ( +-  0.01% )

2
perf
Lớp B

@B Layer - chỉnh sửa câu trả lời của tôi để mô tả ngắn gọn về 'perf'.
Zaahid

1
perf statbây giờ phàn nàn về "Bạn có thể không được phép thu thập số liệu thống kê." trừ khi bạn chạy một số lệnh với sudo, điều này làm cho nó vô dụng trong tất cả các tình huống khi bạn không hoàn toàn sở hữu máy mục tiêu.
alamar

4

Một hàm shell nhỏ có thể được thêm vào trước các lệnh để đo thời gian của chúng:

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

Sau đó sử dụng nó trong tập lệnh của bạn hoặc trên dòng lệnh của bạn như vậy:

tm the_original_command with all its parameters

Nó sẽ có giá trị để thêm mili giây, đã thử ilke s=$(date +%s000), không chắc nó có hoạt động không.
loretoparisi

3

Đây là một biến thể của câu trả lời của Alex. Tôi chỉ quan tâm đến phút và giây, nhưng tôi cũng muốn nó được định dạng khác nhau. Vì vậy, tôi đã làm điều này:

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

1
Tại sao các downvote? Tôi có một lỗi?
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 shihcheng.guo@gmail.com
#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..."

7
Làm thế nào điều này thực sự khác biệt với các câu trả lời khác đã được đưa ra sử dụng datetrước và sau tập lệnh và đưa ra sự khác biệt giữa chúng?
Eric Renouf

2

Chỉ sử dụng bash, cũng có thể đo và tính thời lượng cho một phần của tập lệnh shell (hoặc thời gian đã trôi qua cho toàn bộ tập lệnh):

start=$SECONDS

... # do time consuming stuff

end=$SECONDS

bây giờ bạn có thể chỉ cần in sự khác biệt:

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

nếu bạn chỉ cần thời lượng gia tăng thì làm:

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

Bạn cũng có thể lưu trữ thời lượng trong một biến:

let diff=end-start

^ Đây là câu trả lời folks. Hoặc đơn giản hơn: $ SECONDS ở cuối. Đó là biến BUILT-IN Bash. Tất cả các câu trả lời khác chỉ là làm thêm để phát minh lại bánh xe này ...
Crossfit_and_Beer

2

Cá nhân, tôi thích bọc tất cả mã tập lệnh của mình trong một số chức năng "chính" như vậy:

main () {
 echo running ...
}

# stuff ...

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

Lưu ý cách dễ dàng sử dụng timelệnh trong kịch bản này. Rõ ràng là bạn không đo thời gian chính xác bao gồm cả thời gian phân tích kịch bản, nhưng tôi thấy nó đủ chính xác trong hầu hết các tình huống.


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

1

Hàm thời gian dựa trên SECONDS, chỉ giới hạn ở mức độ chi tiết cấp hai, không sử dụng bất kỳ lệnh bên ngoài nào:

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

Ví dụ:

time_it sleep 5

cho

2019-03-30_17:24:37 Starting sleep 5 2019-03-30_17:24:42 Finished
sleep 5; elapsed = 5 seconds
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.