Thông thường tôi sử dụng lệnh shell time
. Mục đích của tôi là để kiểm tra xem dữ liệu là tập hợp nhỏ, trung bình, lớn hoặc rất lớn, thời gian sử dụng và bộ nhớ sẽ là bao nhiêu.
Bất kỳ công cụ nào cho linux hoặc chỉ python để làm điều này?
Câu trả lời:
Có một cái nhìn tại timeit , các hồ sơ python và pycallgraph . Ngoài ra, hãy xem phần bình luận bên dưới bằng cáchnikicc
đề cập đến " SnakeViz ". Nó cung cấp cho bạn một hình dung khác về dữ liệu hồ sơ có thể hữu ích.
def test():
"""Stupid test function"""
lst = []
for i in range(100):
lst.append(i)
if __name__ == '__main__':
import timeit
print(timeit.timeit("test()", setup="from __main__ import test"))
# For Python>=3.5 one can also write:
print(timeit.timeit("test()", globals=locals()))
Về cơ bản, bạn có thể chuyển mã python dưới dạng tham số chuỗi và nó sẽ chạy trong khoảng thời gian được chỉ định và in thời gian thực thi. Các bit quan trọng từ tài liệu :
timeit.timeit(stmt='pass', setup='pass', timer=<default timer>, number=1000000, globals=None)
Tạo mộtTimer
thể hiện với câu lệnh đã cho, mã thiết lập và chức năng hẹn giờ và chạytimeit
phương thức của nó với số lần thực thi. Các tùy chọn globals lập luận xác định một không gian tên trong đó để thực thi mã.
... và:
Timer.timeit(number=1000000)
Số thời gian thực hiện câu lệnh chính. Điều này thực hiện câu lệnh thiết lập một lần, và sau đó trả về thời gian cần thiết để thực hiện câu lệnh chính một số lần, được đo bằng giây dưới dạng số thực. Đối số là số lần qua vòng lặp, mặc định là một triệu. Câu lệnh chính, câu lệnh thiết lập và hàm bộ đếm thời gian được sử dụng được chuyển cho hàm khởi tạo.Lưu ý: Theo mặc định,
timeit
tạm thời tắtgarbage collection
trong thời gian. Ưu điểm của phương pháp này là nó làm cho các thời gian độc lập có thể so sánh được với nhau. Nhược điểm này là GC có thể là một thành phần quan trọng của hiệu suất của chức năng được đo. Nếu vậy, GC có thể được bật lại dưới dạng câu lệnh đầu tiên trong chuỗi thiết lập . Ví dụ:
timeit.Timer('for i in xrange(10): oct(i)', 'gc.enable()').timeit()
Profiling sẽ cung cấp cho bạn một nhiều ý tưởng chi tiết hơn về những gì đang xảy ra. Đây là "ví dụ tức thì" từ các tài liệu chính thức :
import cProfile
import re
cProfile.run('re.compile("foo|bar")')
Cái nào sẽ cung cấp cho bạn:
197 function calls (192 primitive calls) in 0.002 seconds
Ordered by: standard name
ncalls tottime percall cumtime percall filename:lineno(function)
1 0.000 0.000 0.001 0.001 <string>:1(<module>)
1 0.000 0.000 0.001 0.001 re.py:212(compile)
1 0.000 0.000 0.001 0.001 re.py:268(_compile)
1 0.000 0.000 0.000 0.000 sre_compile.py:172(_compile_charset)
1 0.000 0.000 0.000 0.000 sre_compile.py:201(_optimize_charset)
4 0.000 0.000 0.000 0.000 sre_compile.py:25(_identityfunction)
3/1 0.000 0.000 0.000 0.000 sre_compile.py:33(_compile)
Cả hai mô-đun này sẽ cung cấp cho bạn ý tưởng về nơi cần tìm các điểm nghẽn.
Ngoài ra, để nắm rõ đầu ra của profile
, hãy xem bài đăng này
LƯU Ý pycallgraph đã chính thức bị bỏ rơi kể từ tháng 2 năm 2018 . Mặc dù vậy, kể từ tháng 12 năm 2020, nó vẫn đang hoạt động trên Python 3.6. Miễn là không có thay đổi cốt lõi nào về cách python hiển thị API hồ sơ thì nó vẫn là một công cụ hữu ích.
Mô-đun này sử dụng graphviz để tạo các biểu đồ như sau:
Bạn có thể dễ dàng xem đường dẫn nào được sử dụng nhiều nhất theo màu sắc. Bạn có thể tạo chúng bằng cách sử dụng API pycallgraph hoặc sử dụng tập lệnh đóng gói:
pycallgraph graphviz -- ./mypythonscript.py
Tuy nhiên, chi phí là khá đáng kể. Vì vậy, đối với các quy trình đã chạy lâu, việc tạo biểu đồ có thể mất một chút thời gian.
Tôi sử dụng một trình trang trí đơn giản để tính giờ cho func
def st_time(func):
"""
st decorator to calculate the total time of a func
"""
def st_func(*args, **keyArgs):
t1 = time.time()
r = func(*args, **keyArgs)
t2 = time.time()
print "Function=%s, Time=%s" % (func.__name__, t2 - t1)
return r
return st_func
Các timeit
mô-đun là chậm và kỳ lạ, vì vậy tôi đã viết này:
def timereps(reps, func):
from time import time
start = time()
for i in range(0, reps):
func()
end = time()
return (end - start) / reps
Thí dụ:
import os
listdir_time = timereps(10000, lambda: os.listdir('/'))
print "python can do %d os.listdir('/') per second" % (1 / listdir_time)
Đối với tôi, nó nói:
python can do 40925 os.listdir('/') per second
Đây là một loại điểm chuẩn sơ khai, nhưng nó đủ tốt.
Tôi thường làm nhanh time ./script.py
để xem mất bao lâu. Tuy nhiên, điều đó không hiển thị cho bạn bộ nhớ, ít nhất không phải là bộ nhớ mặc định. Bạn có thể sử dụng /usr/bin/time -v ./script.py
để lấy nhiều thông tin, bao gồm cả việc sử dụng bộ nhớ.
/usr/bin/time
với -v
tùy chọn này không có sẵn như mặc định trong nhiều bản phân phối, phải được cài đặt. sudo apt-get install time
trong debian, ubuntu vv pacman -S time
archlinux
Hồ sơ bộ nhớ cho tất cả các nhu cầu bộ nhớ của bạn.
https://pypi.python.org/pypi/memory_profiler
Chạy cài đặt pip:
pip install memory_profiler
Nhập thư viện:
import memory_profiler
Thêm người trang trí vào mục bạn muốn tạo hồ sơ:
@profile
def my_func():
a = [1] * (10 ** 6)
b = [2] * (2 * 10 ** 7)
del b
return a
if __name__ == '__main__':
my_func()
Thực thi mã:
python -m memory_profiler example.py
Nhận đầu ra:
Line # Mem usage Increment Line Contents
==============================================
3 @profile
4 5.97 MB 0.00 MB def my_func():
5 13.61 MB 7.64 MB a = [1] * (10 ** 6)
6 166.20 MB 152.59 MB b = [2] * (2 * 10 ** 7)
7 13.61 MB -152.59 MB del b
8 13.61 MB 0.00 MB return a
Ví dụ từ các tài liệu, được liên kết ở trên.
Hãy nhìn vào mũi và tại một trong các plugin của nó, cái này nói riêng.
Sau khi cài đặt, mũi là một tập lệnh trong đường dẫn của bạn và bạn có thể gọi trong một thư mục chứa một số tập lệnh python:
$: nosetests
Điều này sẽ tìm kiếm trong tất cả các tệp python trong thư mục hiện tại và sẽ thực thi bất kỳ chức năng nào mà nó nhận dạng là kiểm tra: ví dụ: nó nhận ra bất kỳ chức năng nào có từ test_ trong tên của nó là kiểm tra.
Vì vậy, bạn chỉ có thể tạo một tập lệnh python có tên là test_your Chức năng.py và viết một cái gì đó như thế này trong đó:
$: cat > test_yourfunction.py
def test_smallinput():
yourfunction(smallinput)
def test_mediuminput():
yourfunction(mediuminput)
def test_largeinput():
yourfunction(largeinput)
Sau đó, bạn phải chạy
$: nosetest --with-profile --profile-stats-file yourstatsprofile.prof testyourfunction.py
và để đọc tệp hồ sơ, hãy sử dụng dòng python này:
python -c "import hotshot.stats ; stats = hotshot.stats.load('yourstatsprofile.prof') ; stats.sort_stats('time', 'calls') ; stats.print_stats(200)"
nose
dựa vào hotshot. Nó không còn được duy trì kể từ Python 2.5 và chỉ giữ "để sử dụng chuyên ngành"
Be carefull timeit
rất chậm, phải mất 12 giây trên bộ xử lý trung bình của tôi để khởi tạo (hoặc có thể chạy chức năng). bạn có thể kiểm tra câu trả lời được chấp nhận này
def test():
lst = []
for i in range(100):
lst.append(i)
if __name__ == '__main__':
import timeit
print(timeit.timeit("test()", setup="from __main__ import test")) # 12 second
vì điều đơn giản tôi sẽ sử dụng time
thay thế, trên PC của tôi, nó trả về kết quả0.0
import time
def test():
lst = []
for i in range(100):
lst.append(i)
t1 = time.time()
test()
result = time.time() - t1
print(result) # 0.000000xxxx
timeit
chạy chức năng của bạn nhiều lần, để loại bỏ tiếng ồn trung bình. Số lần lặp lại là một tùy chọn, hãy xem Đo điểm chuẩn thời gian chạy trong python hoặc phần sau của câu trả lời được chấp nhận cho câu hỏi này.
snakeviz
trình xem tương tác cho cProfile
https://github.com/jiffyclub/snakeviz/
cProfile đã được đề cập tại https://stackoverflow.com/a/1593034/895245 và rắnviz đã được đề cập trong một nhận xét , nhưng tôi muốn làm nổi bật nó hơn nữa.
Rất khó để gỡ lỗi hiệu suất chương trình chỉ bằng cách nhìn vào cprofile
/ pstats
đầu ra, bởi vì chúng chỉ có thể tổng số lần cho mỗi chức năng ra khỏi hộp.
Tuy nhiên, những gì chúng ta thực sự cần nói chung là xem một chế độ xem lồng nhau chứa các dấu vết ngăn xếp của mỗi lệnh gọi để thực sự dễ dàng tìm thấy các nút thắt cổ chai chính.
Và đây chính xác là những gì Snakeviz cung cấp thông qua chế độ xem "icicle" mặc định của nó.
Trước tiên, bạn phải kết xuất dữ liệu cProfile vào một tệp nhị phân, và sau đó bạn có thể xử lý dữ liệu đó
pip install -u snakeviz
python -m cProfile -o results.prof myscript.py
snakeviz results.prof
Thao tác này in một URL tới stdout mà bạn có thể mở trên trình duyệt của mình, chứa kết quả đầu ra mong muốn giống như sau:
và sau đó bạn có thể:
Câu hỏi định hướng hồ sơ khác: Làm thế nào bạn có thể lập hồ sơ một tập lệnh Python?
Nếu bạn không muốn viết mã soạn sẵn cho thời gian và dễ dàng phân tích kết quả, hãy xem điểm chuẩn . Ngoài ra, nó còn lưu lại lịch sử của các lần chạy trước đó, vì vậy dễ dàng so sánh cùng một chức năng trong quá trình phát triển.
# pip install benchmarkit
from benchmarkit import benchmark, benchmark_run
N = 10000
seq_list = list(range(N))
seq_set = set(range(N))
SAVE_PATH = '/tmp/benchmark_time.jsonl'
@benchmark(num_iters=100, save_params=True)
def search_in_list(num_items=N):
return num_items - 1 in seq_list
@benchmark(num_iters=100, save_params=True)
def search_in_set(num_items=N):
return num_items - 1 in seq_set
benchmark_results = benchmark_run(
[search_in_list, search_in_set],
SAVE_PATH,
comment='initial benchmark search',
)
In ra thiết bị đầu cuối và trả về danh sách các từ điển có dữ liệu cho lần chạy cuối cùng. Các điểm nhập dòng lệnh cũng có sẵn.
Nếu bạn thay đổi N=1000000
và chạy lại
python -m cProfile -o results.prof myscript.py
. Các tập tin oputput sau đó có thể rất độc đáo được trình bày trong một trình duyệt bởi một chương trình gọi là SnakeViz sử dụngsnakeviz results.prof