Làm thế nào tôi có thể thời gian một đường ống?


27

Tôi muốn timemột lệnh bao gồm hai lệnh riêng biệt với một đầu ra đường ống khác. Ví dụ, hãy xem xét hai tập lệnh dưới đây:

$ cat foo.sh
#!/bin/sh
sleep 4

$ cat bar.sh
#!/bin/sh
sleep 2

Bây giờ, làm thế nào tôi có timethể báo cáo thời gian thực hiện foo.sh | bar.sh(và vâng, tôi biết đường ống không có ý nghĩa gì ở đây, nhưng đây chỉ là một ví dụ)? Nó hoạt động như mong đợi nếu tôi chạy chúng tuần tự trong một lớp con mà không cần đường ống:

$ time ( foo.sh; bar.sh )

real    0m6.020s
user    0m0.010s
sys     0m0.003s

Nhưng tôi không thể làm cho nó hoạt động khi đường ống:

$ time ( foo.sh | bar.sh )

real    0m4.009s
user    0m0.007s
sys     0m0.003s

$ time ( { foo.sh | bar.sh; } )

real    0m4.008s
user    0m0.007s
sys     0m0.000s

$ time sh -c "foo.sh | bar.sh "

real    0m4.006s
user    0m0.000s
sys     0m0.000s

Tôi đã đọc qua một câu hỏi tương tự ( Cách chạy thời gian trên nhiều lệnh VÀ ghi đầu ra thời gian vào tệp? ) Và cũng đã thử timethực thi độc lập :

$ /usr/bin/time -p sh -c "foo.sh | bar.sh"
real 4.01
user 0.00
sys 0.00

Nó thậm chí không hoạt động nếu tôi tạo tập lệnh thứ ba chỉ chạy đường ống:

$ cat baz.sh
#!/bin/sh
foo.sh | bar.sh

Và rồi thời gian đó:

$ time baz.sh

real    0m4.009s
user    0m0.003s
sys     0m0.000s

Thật thú vị, nó không xuất hiện như thể timethoát ra ngay sau khi lệnh đầu tiên được thực hiện. Nếu tôi đổi bar.shthành:

#!/bin/sh
sleep 2
seq 1 5

Và sau đó time, một lần nữa, tôi đã mong đợi timeđầu ra sẽ được in trước seqnhưng nó không phải là:

$ time ( { foo.sh | bar.sh; } )
1
2
3
4
5

real    0m4.005s
user    0m0.003s
sys     0m0.000s

Có vẻ như timekhông tính thời gian thực hiện bar.shmặc dù phải đợi nó hoàn thành trước khi in báo cáo 1 .

Tất cả các thử nghiệm đã được chạy trên hệ thống Arch và sử dụng bash 4.4.12 (1). Tôi chỉ có thể sử dụng bash cho dự án, đây là một phần vì vậy ngay cả khi zshhoặc một số vỏ mạnh mẽ khác có thể đi xung quanh nó, đó sẽ không phải là một giải pháp khả thi đối với tôi.

Vì vậy, làm thế nào tôi có thể có được thời gian một tập hợp các lệnh được thực hiện để chạy? Và, trong khi chúng ta đang ở đó, tại sao nó không hoạt động? Có vẻ như timengay lập tức thoát ra ngay sau khi lệnh đầu tiên kết thúc. Tại sao?

Tôi biết tôi có thể có được thời gian cá nhân với một cái gì đó như thế này:

( time foo.sh ) 2>foo.time | ( time bar.sh ) 2> bar.time

Nhưng tôi vẫn muốn biết liệu có thể tính thời gian cho toàn bộ hoạt động như một hoạt động không.


1 này dường như không phải là một vấn đề đệm, tôi đã cố gắng chạy các script với unbufferedstdbuf -i0 -o0 -e0và những con số vẫn còn in trước khi timeđầu ra.


Bạn đã thử nó với một chiếc đồng hồ bấm giờ vật lý?
pericynthion

@pericynthion yep cuối cùng tôi đã làm. Và điều đó cũng cho thấy những gì câu trả lời giải thích: thời gian thực sự hoạt động nhưng (rõ ràng là đủ và như tôi nên nhận ra) các lệnh trong đường ống chạy đồng thời để thời gian thực chất là thời gian chậm nhất.
terdon

Câu trả lời:


33

đang làm việc

Các phần khác nhau của một đường ống được thực hiện đồng thời. Điều duy nhất đồng bộ hóa / tuần tự hóa các quy trình trong đường ống là IO, tức là một quá trình ghi vào quy trình tiếp theo trong đường ống và quy trình tiếp theo đọc những gì quy trình đầu tiên viết. Ngoài ra, họ đang thực hiện độc lập với nhau.

Vì không có đọc hoặc viết xảy ra giữa các quy trình trong đường ống của bạn, nên thời gian để thực hiện đường ống là sleepcuộc gọi dài nhất .

Bạn cũng có thể đã viết

time ( foo.sh & bar.sh &; wait )

Terdon đã đăng một vài đoạn script ví dụ được sửa đổi một chút trong cuộc trò chuyện :

#!/bin/sh
# This is "foo.sh"
echo 1; sleep 1
echo 2; sleep 1
echo 3; sleep 1
echo 4

#!/bin/sh
# This is "bar.sh"
sleep 2
while read line; do
  echo "LL $line"
done
sleep 1

Truy vấn là "tại sao time ( sh foo.sh | sh bar.sh )trả về 4 giây thay vì 3 + 3 = 6 giây?"

Để xem những gì đang xảy ra, bao gồm thời gian gần đúng mà mỗi lệnh được thực thi, người ta có thể làm điều này (đầu ra chứa các chú thích của tôi):

$ time ( env PS4='$SECONDS foo: ' sh -x foo.sh | PS4='$SECONDS bar: ' sh -x bar.sh )
0 bar: sleep 2
0 foo: echo 1     ; The output is buffered
0 foo: sleep 1
1 foo: echo 2     ; The output is buffered
1 foo: sleep 1
2 bar: read line  ; "bar" wakes up and reads the two first echoes
2 bar: echo LL 1
LL 1
2 bar: read line
2 bar: echo LL 2
LL 2
2 bar: read line  ; "bar" waits for more
2 foo: echo 3     ; "foo" wakes up from its second sleep
2 bar: echo LL 3
LL 3
2 bar: read line
2 foo: sleep 1
3 foo: echo 4     ; "foo" does the last echo and exits
3 bar: echo LL 4
LL 4
3 bar: read line  ; "bar" fails to read more
3 bar: sleep 1    ; ... and goes to sleep for one second

real    0m4.14s
user    0m0.00s
sys     0m0.10s

Vì vậy, để kết luận, các đường ống dẫn mất 4 giây, không phải 6, do đệm của đầu ra của hai cuộc gọi đầu tiên echotrong foo.sh.


1
@terdon các giá trị là tổng, nhưng các tập lệnh chiếm rất ít thời gian của người dùng và hệ thống - chúng chỉ chờ, không tính (trừ thời gian đồng hồ treo tường).
Stephen Kitt

2
Lưu ý rằng một số vỏ như vỏ Bourne hoặc ksh93chỉ chờ thành phần cuối cùng của đường ống ( sleep 3 | sleep 1sẽ kéo dài 1 giây). Shell Bourne không có timetừ khóa, nhưng trong ksh93khi chạy với time, tất cả các thành phần được chờ đợi.
Stéphane Chazelas

3
Tôi chỉ nói rằng người ta có thể ngạc nhiên khi thấy rằng sleep 10 | sleep 1mất một giây trong khi time sleep 10 | sleep 1mất 10 giây trong ksh93. Trong vỏ Bourne time sleep 10 | sleep 1sẽ mất một giây, nhưng bạn sẽ nhận được đầu ra thời gian ( sleep 10chỉ và từ /usr/bin/time) ra khỏi màu xanh 9 giây sau đó.
Stéphane Chazelas

1
Đó không phải là về bảo vệ bất cứ điều gì. timelần chính xác đường ống, nhưng thay đổi hành vi của vỏ trong ksh93. (sleep 10 | sleep 1)mất 1 giây, time (sleep 10 | sleep 1)mất 10 giây. { (sleep 10 | sleep 1); echo x; }đầu ra xsau 1 giây, time { (sleep 10 | sleep 1); echo x; }đầu ra xsau 10 giây. Tương tự nếu bạn đặt mã đó trong một hàm và thời gian cho hàm.
Stéphane Chazelas

1
Lưu ý rằng ksh93giống như trong zsh( -o promptsubstở đây), bạn có thể làm typeset -F SECONDSđể có được số giây gần đúng hơn (POSIX shkhông có SECONDS)
Stéphane Chazelas

10

Đây sẽ là một ví dụ tốt hơn?

$ time perl -e 'alarm(3); 1 while 1;' | perl -e 'alarm(4); 1 while 1;'
Alarm clock

real    0m4.004s
user    0m6.992s
sys     0m0.004s

Các tập lệnh busyloop trong 3 và 4 giây (tương ứng), mất tổng cộng 4 giây trong thời gian thực vì thực thi song song và 7 giây thời gian CPU. (ít nhất là khoảng.)

Hoặc này:

$ time ( sleep 2; echo) | ( read x; sleep 3 )

real    0m5.004s
user    0m0.000s
sys     0m0.000s

Chúng không chạy song song, vì vậy tổng thời gian thực hiện là 5 giây. Tất cả đều dành cho việc ngủ, vì vậy không có thời gian sử dụng CPU.


3

Nếu bạn có, sysdigbạn có thể chèn các bộ theo dõi tại các điểm tùy ý, giả sử bạn có thể sửa đổi mã để thêm ghi cần thiết vào/dev/null

echo '>::blah::' >/dev/null
foo.sh | bar.sh
echo '<::blah::' >/dev/null

(nhưng điều đó không thành công yêu cầu "hoạt động đơn lẻ" của bạn) và sau đó ghi lại mọi thứ thông qua

$ sudo sysdig -w blalog "span.tags contains blah"

và sau đó có lẽ bạn sẽ cần một cái đục sysdig để xuất chỉ thời lượng

description = "Exports sysdig span tag durations";
short_description = "Export span tag durations.";
category = "Tracers";

args = {}

function on_init()
    ftags = chisel.request_field("span.tags")
    flatency = chisel.request_field("span.duration")
    chisel.set_filter("evt.type=tracer and evt.dir=<")
    return true
end

function on_event()
    local tags = evt.field(ftags)
    local latency = evt.field(flatency)
    if latency then
        print(tostring(tags) .. "\t" .. tonumber(latency) / 1e9)
    end
    return true
end

mà một khi được lưu vào sysdig/chiselsthư mục của bạn như tập tin spantagduration.luacó thể được sử dụng như

$ sysdig -r blalog -c spantagduration
...

Hoặc bạn có thể chơi xung quanh với csysdighoặc đầu ra JSON.

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.