Thời gian của python.sleep () chính xác đến mức nào?


95

Tôi có thể cung cấp cho nó số dấu phẩy động, chẳng hạn như

time.sleep(0.5)

nhưng độ chính xác của nó là bao nhiêu? Nếu tôi đưa nó

time.sleep(0.05)

nó sẽ thực sự ngủ khoảng 50 ms?

Câu trả lời:


78

Độ chính xác của chức năng time.sleep phụ thuộc vào độ chính xác khi ngủ của hệ điều hành cơ bản của bạn. Đối với hệ điều hành không thời gian thực như Windows cổ phiếu, khoảng thời gian nhỏ nhất bạn có thể ngủ là khoảng 10-13ms. Tôi đã thấy những giấc ngủ chính xác trong vòng vài mili giây của thời gian đó khi trên mức tối thiểu 10-13ms.

Cập nhật: Giống như được đề cập trong các tài liệu được trích dẫn bên dưới, thông thường bạn sẽ thực hiện chế độ ngủ theo vòng lặp để đảm bảo bạn sẽ quay lại ngủ nếu nó đánh thức bạn sớm.

Tôi cũng nên đề cập rằng nếu bạn đang chạy Ubuntu, bạn có thể thử một nhân thời gian thực giả (với bộ bản vá RT_PREEMPT) bằng cách cài đặt gói nhân rt (ít nhất là trong Ubuntu 10.04 LTS).

CHỈNH SỬA: Sửa chữa các hạt nhân Linux không theo thời gian thực có khoảng thời gian nghỉ tối thiểu gần 1ms rồi 10ms nhưng nó thay đổi theo cách không xác định.


8
Trên thực tế, hạt nhân Linux đã mặc định có tỷ lệ đánh dấu cao hơn trong một thời gian khá dài, vì vậy giấc ngủ "tối thiểu" gần 1ms hơn 10ms. Nó không được đảm bảo - hoạt động khác của hệ thống có thể khiến hạt nhân không thể lên lịch xử lý của bạn ngay khi bạn muốn, ngay cả khi không có sự tranh cãi của CPU. Tôi nghĩ đó là điều mà các hạt nhân thời gian thực đang cố gắng sửa chữa. Tuy nhiên, trừ khi bạn thực sự cần hành vi thời gian thực, chỉ cần sử dụng tỷ lệ đánh dấu cao (cài đặt kernel HZ) sẽ giúp bạn ngủ không được đảm bảo nhưng độ phân giải cao trong Linux mà không cần sử dụng bất kỳ thứ gì đặc biệt.
Glenn Maynard

1
Vâng, bạn nói đúng, tôi đã thử với Linux 2.6.24-24 và có thể đạt được tốc độ cập nhật khá gần 1000 Hz. Tại thời điểm tôi làm việc này, tôi cũng đang chạy mã trên Mac và Windows, vì vậy tôi có thể bị nhầm lẫn. Tôi biết windows XP ít nhất có tốc độ đánh dấu khoảng 10ms.
Joseph Lisee

Trên Windows 8, tôi chỉ nhận được dưới 2ms
markmnl

2
Ngoài ra, độ chính xác không chỉ phụ thuộc vào HĐH mà là HĐH đang làm gì trên cả Windows và Linux nếu họ đang bận làm việc gì đó quan trọng hơn sleep()từ tài liệu "thời gian tạm ngưng có thể lâu hơn yêu cầu bởi một lượng tùy ý vì lịch trình của hoạt động khác trong hệ thống ”.
markmnl

55

Mọi người khá đúng về sự khác biệt giữa hệ điều hành và hạt nhân, nhưng tôi không thấy bất kỳ mức độ chi tiết nào trong Ubuntu và tôi thấy mức độ chi tiết 1 ms trong MS7. Đề xuất một cách triển khai time.sleep khác, không chỉ một tỷ lệ đánh dấu khác. Kiểm tra kỹ hơn cho thấy mức độ chi tiết 1μs trong Ubuntu, nhưng đó là do chức năng time.time mà tôi sử dụng để đo độ chính xác. Hành vi time.sleep thông thường của Linux và Windows trong Python


6
Thật thú vị khi Linux đã chọn luôn ngủ lâu hơn một chút so với yêu cầu, trong khi Microsoft lại chọn cách tiếp cận ngược lại.
jleahy

2
@jleahy - cách tiếp cận linux có ý nghĩa với tôi: sleep thực sự là một giải phóng quyền ưu tiên thực thi trong một khoảng thời gian mà sau đó bạn một lần nữa phục tùng ý muốn của người lập lịch (có thể lên lịch cho bạn thực hiện ngay lập tức) .
underrun

2
làm thế nào bạn nhận được kết quả? Bạn có thể cung cấp mã nguồn không? Biểu đồ trông giống như một tạo tác của việc sử dụng các bộ hẹn giờ khác nhau để đo thời gian và giấc ngủ (Về nguyên tắc, bạn thậm chí có thể sử dụng độ lệch giữa các bộ hẹn giờ như một nguồn ngẫu nhiên ).
jfs

1
@JF Sebastian - Hàm mà tôi đã sử dụng nằm trong socsci.ru.nl/wilberth/computer/sleepAccuracy.html . Biểu đồ thứ ba ở đó cho thấy một hiệu ứng tương tự như những gì bạn thấy, nhưng chỉ là 1 ‰.
Wilbert

1
@JF Sebastian Tôi sử dụng time.clock () trên windows
Wilbert

26

Từ tài liệu :

Mặt khác, độ chính xác của time()sleep()tốt hơn các điểm tương đương Unix của chúng: thời gian được biểu thị dưới dạng số dấu phẩy động, time()trả về thời gian chính xác nhất hiện có (sử dụng Unix gettimeofday nếu có) và sleep()sẽ chấp nhận thời gian có phân số khác 0 (Unix selectđược sử dụng để thực hiện điều này, nếu có).

cụ thể hơn là wrt sleep():

Tạm dừng thực thi trong số giây nhất định. Đối số có thể là một số dấu phẩy động để chỉ ra thời gian ngủ chính xác hơn. Thời gian tạm ngừng thực tế có thể ít hơn thời gian yêu cầu vì bất kỳ tín hiệu bắt được nào sẽ chấm dứt việc sleep()thực hiện quy trình bắt tín hiệu đó sau đó. Ngoài ra, thời gian tạm ngừng có thể lâu hơn yêu cầu bởi một số lượng tùy ý vì lịch trình của hoạt động khác trong hệ thống.


1
Bất cứ ai có thể giải thích "bởi vì bất kỳ tín hiệu bắt được sẽ kết thúc giấc ngủ () sau khi thực hiện thói quen bắt của tín hiệu đó"? Nó đề cập đến những tín hiệu nào? Cảm ơn!
Diego Herranz

1
Tín hiệu giống như các thông báo mà HĐH quản lý ( en.wikipedia.org/wiki/Unix_signal ), có nghĩa là nếu HĐH bắt được tín hiệu thì chế độ ngủ () sẽ kết thúc sau khi xử lý tín hiệu đó.
ArianJM

24

Đây là phần tiếp theo của tôi đối với câu trả lời của Wilbert: Mac OS X Yosemite cũng vậy, vì nó vẫn chưa được đề cập nhiều.Hoạt động ngủ của Mac OS X Yosemite

Có vẻ như rất nhiều thời gian nó ngủ gấp khoảng 1,25 lần thời gian bạn yêu cầu và đôi khi ngủ gấp từ 1 đến 1,25 lần thời gian bạn yêu cầu. Nó gần như không bao giờ (~ hai lần trong số 1000 mẫu) ngủ nhiều hơn đáng kể 1,25 lần thời gian bạn yêu cầu.

Ngoài ra (không được hiển thị rõ ràng) mối quan hệ 1,25 dường như giữ khá tốt cho đến khi bạn xuống dưới khoảng 0,2 ms, sau đó nó bắt đầu hơi mờ. Ngoài ra, thời gian thực tế dường như dài hơn khoảng 5 mili giây so với yêu cầu của bạn sau khi lượng thời gian yêu cầu vượt quá 20 mili giây.

Một lần nữa, nó dường như là một cách triển khai hoàn toàn khác sleep()trong OS X so với trong Windows hoặc bất kỳ Linux nào kernal Wilbert đang sử dụng.


Bạn có thể tải mã nguồn cho điểm chuẩn lên github / bitbucket không?
jfs

3
Tôi đã thử trên máy của mình. Kết quả tương tự với câu trả lời của @ Wilbert .
jfs

Tôi đoán rằng bản thân chế độ ngủ là chính xác nhưng lập lịch của Mac OS X không đủ chính xác để cung cấp CPU đủ nhanh để việc đánh thức từ chế độ ngủ bị trì hoãn. Nếu thời gian thức dậy chính xác là quan trọng, có vẻ như giấc ngủ nên được đặt thành 0,75 lần so với thực tế được yêu cầu và kiểm tra thời gian sau khi thức dậy và ngủ liên tục ngày càng ít cho đến đúng thời điểm.
Mikko Rantalainen

16

Tại sao bạn không tìm hiểu:

from datetime import datetime
import time

def check_sleep(amount):
    start = datetime.now()
    time.sleep(amount)
    end = datetime.now()
    delta = end-start
    return delta.seconds + delta.microseconds/1000000.

error = sum(abs(check_sleep(0.050)-0.050) for i in xrange(100))*10
print "Average error is %0.2fms" % error

Đối với hồ sơ, tôi gặp lỗi khoảng 0,1ms trên HTPC và 2ms trên máy tính xách tay của tôi, cả hai máy Linux.


10
Thử nghiệm thực nghiệm sẽ cung cấp cho bạn một cái nhìn rất hẹp. Có nhiều nhân, hệ điều hành và cấu hình nhân ảnh hưởng đến điều này. Các hạt nhân Linux cũ hơn mặc định có tỷ lệ đánh dấu thấp hơn, dẫn đến độ chi tiết cao hơn. Trong quá trình triển khai Unix, một tín hiệu bên ngoài trong thời gian ngủ sẽ hủy nó bất cứ lúc nào và các triển khai khác có thể bị gián đoạn tương tự.
Glenn Maynard

6
Tất nhiên, quan sát thực nghiệm không thể chuyển nhượng được. Ngoài hệ điều hành và hạt nhân, có rất nhiều vấn đề tạm thời ảnh hưởng đến điều này. Nếu yêu cầu đảm bảo thời gian thực cứng thì cần phải xem xét toàn bộ thiết kế hệ thống từ phần cứng trở lên. Tôi chỉ tìm thấy các kết quả phù hợp khi xem xét các tuyên bố rằng 10ms là độ chính xác tối thiểu. Tôi không ở nhà trong thế giới Windows, nhưng hầu hết các bản phân phối linux đã chạy các hạt nhân vô ích trong một thời gian. Với sự phổ biến hiện nay của đa nhân vật, rất có thể bạn sẽ được lên lịch thực sự gần với thời gian chờ.
Ants Aasma

4

Một điều chỉnh nhỏ, một số người đề cập rằng giấc ngủ có thể kết thúc sớm bằng một tín hiệu. Trong 3,6 tài liệu, nó nói,

Đã thay đổi trong phiên bản 3.5: Chức năng hiện ở chế độ ngủ ít nhất vài giây ngay cả khi chế độ ngủ bị gián đoạn bởi một tín hiệu, ngoại trừ trường hợp trình xử lý tín hiệu đưa ra một ngoại lệ (xem PEP 475 để biết lý do).


3

Bạn thực sự không thể đảm bảo bất cứ điều gì về giấc ngủ (), ngoại trừ việc ít nhất nó sẽ cố gắng hết sức để ngủ miễn là bạn đã nói với nó (các tín hiệu có thể giết chết giấc ngủ của bạn trước khi hết giờ và nhiều thứ khác có thể khiến nó chạy Dài).

Để chắc chắn rằng mức tối thiểu bạn có thể nhận được trên một hệ điều hành máy tính để bàn tiêu chuẩn sẽ là khoảng 16ms (độ chi tiết của bộ đếm thời gian cộng với thời gian chuyển đổi ngữ cảnh), nhưng rất có thể độ lệch% so với đối số được cung cấp sẽ là đáng kể khi bạn đang thử để ngủ trong 10 giây mili giây.

Tín hiệu, các luồng khác giữ GIL, niềm vui lên lịch hạt nhân, bước tốc độ bộ xử lý, v.v. đều có thể tàn phá với thời gian luồng / quy trình của bạn thực sự ngủ.


3
Tài liệu hướng dẫn ngược lại:> Thời gian tạm ngưng thực tế có thể ít hơn thời gian yêu cầu vì bất kỳ tín hiệu bắt được nào sẽ kết thúc chế độ ngủ () sau khi thực hiện quy trình bắt của tín hiệu đó.
Glenn Maynard

Ah điểm công bằng, đã sửa bài đăng, mặc dù thời gian ngủ dài hơn () có nhiều khả năng hơn những giấc ngủ ngắn hơn.
Nick Bastin

1
Hai năm rưỡi sau ... tài liệu vẫn còn. Trên Windows, các tín hiệu sẽ không kết thúc chế độ ngủ (). Đã thử nghiệm trên Python 3.2, WinXP SP3.
Dave

Có nhưng các tín hiệu báo trước chế độ ngủ không sử dụng được, ví dụ như KILL, tài liệu cũng cho biết: "Ngoài ra, thời gian tạm ngừng có thể lâu hơn yêu cầu bởi một lượng tùy ý do lên lịch cho hoạt động khác trong hệ thống." cái nào điển hình hơn.
markmnl

1
Singnals và Windows thật ngớ ngẩn. Trên Windows, Python time.sleep () đợi trên ConsoleEvent để nắm bắt những thứ như Ctrl-C.
schlenk

1

nếu bạn cần chính xác hơn hoặc giảm thời gian ngủ, hãy cân nhắc việc tự làm:

import time

def sleep(duration, get_now=time.perf_counter):
    now = get_now()
    end = now + duration
    while now < end:
        now = get_now()


0
def start(self):
    sec_arg = 10.0
    cptr = 0
    time_start = time.time()
    time_init = time.time()
    while True:
        cptr += 1
        time_start = time.time()
        time.sleep(((time_init + (sec_arg * cptr)) - time_start ))

        # AND YOUR CODE .......
        t00 = threading.Thread(name='thread_request', target=self.send_request, args=([]))
        t00.start()

Không sử dụng một biến để chuyển đối số của sleep (), bạn phải chèn phép tính trực tiếp vào sleep ()


Và sự trở lại của thiết bị đầu cuối của tôi

1 ───── 17: 20: 16.891 ────────────────────

2 ───── 17: 20: 18.891 ────────────────────

3 ───── 17: 20: 20.891 ────────────────────

4 ───── 17: 20: 22.891 ────────────────────

5 ───── 17: 20: 24.891 ────────────────────

....

689 ─── 17: 43: 12.891 ─────────────────────

690 ─── 17: 43: 14.890 ─────────────────────

691 ─── 17: 43: 16.891 ─────────────────────

692 ─── 17: 43: 18.890 ─────────────────────

693 ─── 17: 43: 20.891 ─────────────────────

...

727 ─── 17: 44: 28.891 ─────────────────────

728 ─── 17: 44: 30.891 ─────────────────────

729 ─── 17: 44: 32.891 ─────────────────────

730 ─── 17: 44: 34.890 ─────────────────────

731 ─── 17: 44: 36.891 ─────────────────────

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.