Python Timer: Cách đo thời gian thực thi của tập lệnh Python
15/05/2023 01:19
Trong bài viết này, chúng tôi sẽ chỉ cho bạn cách đo thời gian thực thi của các chương trình Python. Chúng tôi sẽ giới thiệu cho bạn một số công cụ và hiển thị cho bạn các ví dụ hữu ích để minh họa cách đo thời gian của toàn bộ chương trình
Trong bài viết này, chúng tôi sẽ chỉ cho bạn cách đo thời gian thực thi của các chương trình Python. Chúng tôi sẽ giới thiệu cho bạn một số công cụ và hiển thị cho bạn các ví dụ hữu ích để minh họa cách đo thời gian của toàn bộ chương trình, một chức năng đơn lẻ hoặc chỉ một câu lệnh đơn giản. Cuối cùng, bạn sẽ có thể hiểu rõ hơn về cách mã của mình hoạt động bằng cách định thời gian chính xác cho mã.
Mục tiêu của bài viết này là phát triển hai chương trình khác nhau để tính các số Fibonacci và tính thời gian cho cả hai. Nhưng trước khi bắt đầu, chúng ta cần một bài học ngắn về toán học.
Dãy Fibonacci
Dãy Fibonacci là một dãy toán học đẹp và nổi tiếng, dễ hiểu và dễ tính toán. Chuỗi bắt đầu với hai giá trị: 0 và 1. Các giá trị tiếp theo được cộng theo cách đệ quy bằng cách tính tổng hai số cuối cùng trong chuỗi. 12 số Fibonacci đầu tiên là:
0 , 1 , 1 , 2 , 3 , 5 , 8 , 13 , 21 , 34 , 55 , 89 |
Những con số này luôn xuất hiện trong toán học và có ứng dụng trong thế giới thực trong nhiều lĩnh vực, bao gồm khoa học máy tính, quang học và thậm chí cả phân tích tài chính. Dãy số Fibonacci cũng xuất hiện trong thế giới tự nhiên. Điều này đã dẫn đến hình xoắn ốc Fibonacci, là một mô hình tốt cho các hình dạng từ vỏ sò đến các thiên hà xoắn ốc.
Thuật toán để tính số Fibonacci thứ n như sau:
F 0 = 0, F 1 = 1,
Và
F n = F n-1 + F n-2
với n > 1
Mục tiêu của chúng tôi là viết một triển khai Python và thời gian tính toán số Fibonacci thứ n . Trước khi bạn tiếp tục với bài viết này, hãy cố gắng đưa ra một triển khai cho chính mình. (Như một gợi ý, bạn sẽ cần một if-elif-else
câu lệnh.) Hãy nhớ rằng thuật toán là đệ quy, nghĩa là bạn sẽ cần gọi hàm từ bên trong chính nó.
Hai triển khai Python
Bây giờ chúng ta sẽ phát triển hai triển khai khác nhau để tính toán các số Fibonacci và đo thời gian thực hiện của cả hai. Bắt đầu bằng cách mở IDE HOẶC TRÌNH SOẠN THẢO VĂN BẢN YÊU THÍCH của bạn . Viết chương trình sau và lưu tệp với tên fibonacci_v1.py :
def fib_v1(num): if num > 1 : return fib_v1(num - 1 ) + fib_v1(num - 2 ) elif num = = 1 : return 1 else : return 0 fib_v1( 42 ) |
Trong chương trình này, chúng tôi kiểm tra riêng hai số đầu tiên của dãy (0, 1). Nếu không có một trong các giá trị này thì ta tính tổng hai số cuối của dãy. Hàm gọi chính nó nhiều lần để tính từng số Fibonacci trong dãy và do đó khá chậm.
Việc thực hiện thứ hai được hiển thị dưới đây. Nhập mã này vào một tập lệnh Python mới và lưu nó dưới dạng fibonacci_v2.py :
def fib_rec(f1, f2, n): if n = = 0 : return f1 else : return fib_rec(f2, f1 + f2, n - 1 ) def fib_v2(num): return fib_rec( 0 , 1 , num) fib_v2( 42 ) |
Việc triển khai thứ hai chia phép tính thành hai hàm. Hàm đệ quy fib_rec() chỉ gọi chính nó một lần và do đó nhanh hơn so với lần triển khai đầu tiên.
Lệnh thời gian Linux/UNIX
Phương pháp đầu tiên để đo thời gian thực hiện các tập lệnh Python của chúng tôi sử dụng lệnh thời gian UNIX. Chi tiết về điều này sẽ khác nhau tùy thuộc vào bản phân phối bạn đang sử dụng, vì mỗi bản phân phối có trình bao lệnh mặc định khác nhau. Trình bao phổ biến nhất trên các bản phân phối Linux hiện đại là trình bao bash, nhưng cũng có những trình bao khác (ví dụ trình bao Z (zsh) hoặc trình bao Korn (ksh)).
Để đo thời gian thực thi các tập lệnh của chúng tôi, hãy thực hiện lệnh sau từ trình bao bạn chọn:
>>> time python fibonacci_v1.py |
Đầu ra sẽ khác nhau tùy thuộc vào trình bao của bạn, nhưng nó sẽ hiển thị thời gian thực hiện và một số thông tin khác (chẳng hạn như thời gian thực hiện cho cả lệnh gọi hệ thống và phi hệ thống). Thời gian sẽ khác nhau tùy thuộc vào sức mạnh tính toán của hệ thống của bạn, nhưng nó sẽ nằm trong khoảng 30 – 90 giây đối với hầu hết các máy tiêu chuẩn.
Để có được ý tưởng về việc triển khai thứ hai nhanh hơn bao nhiêu, hãy sử dụng lệnh time để tính thời gian thực thi tập lệnh thứ hai:
>>> time python fibonacci_v2.py |
Thời gian thực thi của phiên bản này sẽ nhanh hơn nhiều – khoảng một phần nhỏ của giây.
Phương pháp thời gian rất hữu ích để đo thời gian thực hiện của toàn bộ chương trình từ bên ngoài chương trình. Nhưng có thể có trường hợp bạn muốn đo thời gian bằng Python từ bên trong chương trình. Chúng ta sẽ xem xét kỹ hơn về điều đó.
Mô-đun thời gian của Python
Mô -ĐUN THỜI GIAN là một phần của thư viện chuẩn Python, nghĩa là nó đi kèm với cài đặt mặc định của Python. Nó bao gồm một loạt các chức năng tiện dụng liên quan đến thời gian. Hàm time.sleep() được sử dụng để tạm dừng thực thi chương trình.
Để tính thời gian thực thi tập lệnh bằng mô-đun thời gian, bạn có thể sử dụng hàm time(), hàm này trả về thời gian đã trôi qua tính bằng giây kể từ Kỷ nguyên thời gian UNIX (ngày 1 tháng 1 năm 1970, 00:00:00 UTC). Đối với lần triển khai thứ hai của tập lệnh số Fibonacci, chúng tôi đặt bộ đếm thời gian trước và sau lệnh gọi hàm. Chỉnh sửa tệp fibonacci_v2.py để nó trông giống như sau:
import time def fib_rec(f1, f2, n): if n = = 0 : return f1 else : return fib_rec(f2, f1 + f2, n - 1 ) def fib_v2(num): return fib_rec( 0 , 1 , num) t1 = time.time() fib_v2( 42 ) t2 = time.time() print ( 'Elapsed time: {} seconds' . format ((t2 - t1))) |
Chúng tôi đã nhập time
mô-đun và xác định mô-đun đó hai lần t1
và t2
. Sự khác biệt giữa hai giá trị này cho chúng ta thời gian đã trôi qua mà chúng ta in ở dòng cuối cùng.
Sự hữu ích của phương pháp này nên được rõ ràng. Đầu tiên, bạn không cần dựa vào các chương trình phụ thuộc vào nền tảng để đo thời gian như chúng tôi đã làm trong ví dụ đầu tiên. Thứ hai, bạn có thể đặt bộ hẹn giờ ở bất kỳ đâu trong tập lệnh của mình để đo thời gian thực thi của các phần khác nhau trong mã của bạn. Trong trường hợp này, chỉ cần xác định t1
và t2
ở hai bên của khối mã mà bạn muốn đo thời gian thực hiện.
Nếu bạn chạy phiên bản sửa đổi của tập lệnh fibonacci_v2.py , bạn sẽ nhận thấy thời gian đã trôi qua được in là 0,0 giây. Thời gian được trả về dưới dạng số dấu phẩy động, nhưng không phải tất cả các hệ thống đều cung cấp thời gian với độ chính xác cao.
Mô-đun thời gian cũng có chức time_ns()
năng. Nó tương tự như time()
, nhưng nó trả về thời gian dưới dạng số nguyên nano giây kể từ kỷ nguyên. Tuy nhiên, điều này phụ thuộc vào hệ thống và có thể hoặc không thể cung cấp độ phân giải cao hơn. Để biết kết quả của một số thử nghiệm về thời gian, hãy xem PEP 564 . Trong bài viết của chúng tôi CÁCH LÀM VIỆC VỚI NGÀY VÀ GIỜ TRONG PYTHON , chúng tôi trình bày một ví dụ khác về cách sử dụng hàm sleep()
và time()
.
Phương pháp ưa thích
Chúng tôi để lại những gì tốt nhất để kéo dài. Thư viện chuẩn Python bao gồm một mô-đun khác để định thời gian cho các đoạn mã nhỏ. Đây là MÔ-ĐUN TIMEIT , mô-đun này mạnh hơn một chút và tránh được một số cạm bẫy phổ biến trong việc định thời gian thực thi mã Python của bạn. Thư viện có thể được sử dụng từ dòng lệnh hoặc trong tập lệnh của bạn. Để sử dụng nó từ dòng lệnh, hãy làm như sau:
>>> python - m timeit "1+2" |
Cờ -m
báo cho python chạy timeit
mô-đun dưới dạng tập lệnh. Xem PEP 338 để biết thêm chi tiết. Điều này sẽ chạy câu lệnh (" 1+2
") nhiều lần và báo cáo thời gian tốt nhất. Khi giao diện dòng lệnh được sử dụng, timeit
tự động xác định số lần lặp lại và báo cáo điều này cùng với thời gian tối thiểu.
Để tính thời gian cho fib_v2()
chức năng của chúng ta từ dòng lệnh, chúng ta cần làm như sau:
>>> python - m timeit "from fibonacci_v2 import fib_v2" "fib_v2(42)" |
Đầu ra được in ra dòng lệnh báo cáo thời gian tốt nhất (tức là tối thiểu) và trông giống như thế này:
50000 loops, best of 5 : 6.7 usec per loop |
Lưu ý rằng usec có nghĩa là µsec hoặc micro giây.
Bằng cách này, bạn có thể chạy các câu lệnh đơn lẻ hoặc nhập một hàm và thời gian từ dòng lệnh. Cũng có thể tính thời gian thực hiện một chức năng từ bên trong tập lệnh của bạn.
Một lần nữa, chỉnh sửa tệp fibonacci_v2.py để nó trông giống như sau:
import timeit def fib_rec(f1, f2, n): if n = = 0 : return f1 else : return fib_rec(f2, f1 + f2, n - 1 ) def fib_v2(num): return fib_rec( 0 , 1 , num) n = 50000 total_time = timeit.timeit( 'fib_v2(42)' , number = n, globals = globals ()) print ( 'Average time: {} usec' . format ((total_time / n) * 1e6 )) |
Ở đây chúng tôi đang sử dụng timeit()
chức năng từ timeit
mô-đun. Các đối số stmt
là hàm để thực thi; số, là số lần thực hiện chức năng; và globals
đối số, chỉ định không gian tên để chạy mã. Từ khóa globals () trả về một từ điển gồm tất cả các mô-đun và hàm hiện được tải; chỉ định điều này cho phép chúng tôi truy cập vào chúng. Giá trị trả về là thời gian tích lũy (không phải tối thiểu). Sau khi tính trung bình, đầu ra trông giống như thế này:
Average time: 7.09615 usec |
Khi chạy từ dòng lệnh, kết quả sẽ tốt hơn (vì giao diện dòng lệnh báo cáo thời gian tối thiểu theo mặc định). Cũng đáng lưu ý rằng có những lợi thế khi sử dụng mô-đun timeit so với mô-đun time. timeit được thiết kế để thu được các phép đo chính xác hơn về thời gian thực hiện so với các phép đo từ mô-đun thời gian. Kết quả ít có khả năng bị tác động bởi các yếu tố bên ngoài do hệ điều hành.