Làm thế nào bạn có thể hồ sơ một kịch bản Python?


1283

Project Euler và các cuộc thi mã hóa khác thường có thời gian tối đa để chạy hoặc mọi người tự hào về việc giải pháp cụ thể của họ chạy nhanh như thế nào. Với Python, đôi khi các cách tiếp cận hơi khó hiểu - tức là thêm mã thời gian vào __main__.

Cách tốt để cấu hình chương trình Python mất bao lâu để chạy?


113
Chương trình euler dự án không cần hồ sơ. Hoặc bạn có một thuật toán hoạt động dưới một phút hoặc bạn hoàn toàn sai thuật toán. "Điều chỉnh" hiếm khi thích hợp. Bạn thường phải có một cách tiếp cận mới.
S.Lott

105
S.Lott: Profiling thường là một cách hữu ích để xác định chương trình con nào chậm. Các chương trình con mất nhiều thời gian là ứng cử viên tuyệt vời để cải tiến thuật toán.
stalepretzel

Câu trả lời:


1369

Python bao gồm một profiler gọi là cProfile . Nó không chỉ cung cấp tổng thời gian chạy, mà còn cho từng chức năng riêng biệt và cho bạn biết mỗi chức năng được gọi bao nhiêu lần, giúp bạn dễ dàng xác định nơi bạn nên thực hiện tối ưu hóa.

Bạn có thể gọi nó từ trong mã của bạn hoặc từ trình thông dịch, như thế này:

import cProfile
cProfile.run('foo()')

Thậm chí hữu ích hơn, bạn có thể gọi cProfile khi chạy tập lệnh:

python -m cProfile myscript.py

Để làm cho nó dễ dàng hơn nữa, tôi đã tạo một tệp bó nhỏ có tên 'profile.bat':

python -m cProfile %1

Vì vậy, tất cả những gì tôi phải làm là chạy:

profile euler048.py

Và tôi nhận được điều này:

1007 function calls in 0.061 CPU seconds

Ordered by: standard name
ncalls  tottime  percall  cumtime  percall filename:lineno(function)
    1    0.000    0.000    0.061    0.061 <string>:1(<module>)
 1000    0.051    0.000    0.051    0.000 euler048.py:2(<lambda>)
    1    0.005    0.005    0.061    0.061 euler048.py:2(<module>)
    1    0.000    0.000    0.061    0.061 {execfile}
    1    0.002    0.002    0.053    0.053 {map}
    1    0.000    0.000    0.000    0.000 {method 'disable' of '_lsprof.Profiler objects}
    1    0.000    0.000    0.000    0.000 {range}
    1    0.003    0.003    0.003    0.003 {sum}

EDIT: Liên kết được cập nhật tới một nguồn tài nguyên video tốt từ PyCon 2013 có tiêu đề Python Profiling
Cũng thông qua YouTube .


251
Ngoài ra, rất hữu ích khi sắp xếp kết quả, có thể được thực hiện bằng cách chuyển đổi -s, ví dụ: '-s time'. Bạn có thể sử dụng các tùy chọn sắp xếp tích lũy / tên / thời gian / tập tin.
Jiri

19
Điều đáng chú ý là bạn có thể sử dụng mô-đun cProfile từ ipython bằng cách sử dụng chức năng ma thuật% prun (chạy hồ sơ). Đầu tiên nhập mô-đun của bạn, sau đó gọi hàm chính với% prun: import euler048; % prun euler048.main ()
RussellStewart

53
Để trực quan hóa các bãi chứa cProfile (được tạo bởi python -m cProfile -o <out.profile> <script>), RunSnakeRun , được gọi runsnake <out.profile>là vô giá.
Lily Chung

13
@NeilG ngay cả đối với python 3, cprofilevẫn được khuyến nghị hơn profile.
trichoplax

17
Để trực quan hóa các bãi chứa cProfile, RunSnakeRun đã không được cập nhật từ năm 2011 và không hỗ trợ python3. Thay vào đó, bạn nên sử dụng snakeviz
Giacomo Tecya Pigani

423

Cách đây một thời gian, tôi đã pycallgraphtạo ra một trực quan hóa từ mã Python của bạn. Chỉnh sửa: Tôi đã cập nhật ví dụ để làm việc với 3.3, bản phát hành mới nhất kể từ khi viết bài này.

Sau khi pip install pycallgraphvà cài đặt GraphViz, bạn có thể chạy nó từ dòng lệnh:

pycallgraph graphviz -- ./mypythonscript.py

Hoặc, bạn có thể cấu hình các phần cụ thể của mã của mình:

from pycallgraph import PyCallGraph
from pycallgraph.output import GraphvizOutput

with PyCallGraph(output=GraphvizOutput()):
    code_to_profile()

Một trong hai sẽ tạo ra một pycallgraph.pngtệp tương tự như hình ảnh dưới đây:

nhập mô tả hình ảnh ở đây


43
Bạn đang tô màu dựa trên số lượng cuộc gọi? Nếu vậy, bạn nên tô màu dựa trên thời gian vì chức năng có nhiều cuộc gọi nhất không phải lúc nào cũng mất thời gian nhất.
đỏ

21
@red Bạn có thể tùy chỉnh màu sắc theo cách bạn thích và thậm chí độc lập cho từng phép đo. Ví dụ màu đỏ cho các cuộc gọi, màu xanh lam cho thời gian, màu xanh lá cây cho việc sử dụng bộ nhớ.
gak

2
nhận được lỗi nàyTraceback (most recent call last): /pycallgraph.py", line 90, in generate output.done() File "/net_downloaded/pycallgraph-develop/pycallgraph/output/graphviz.py", line 94, in done source = self.generate() File "/net_downloaded/pycallgraph-develop/pycallgraph/output/graphviz.py", line 143, in generate indent_join.join(self.generate_attributes()), File "/net_downloaded/pycallgraph-develop/pycallgraph/output/graphviz.py", line 169, in generate_attributes section, self.attrs_from_dict(attrs), ValueError: zero length field name in format
Ciasto piekarz

3
Tôi đã cập nhật điều này để đề cập rằng bạn cần cài đặt GraphViz để mọi thứ hoạt động như mô tả. Trên Ubuntu đây chỉ là sudo apt-get install graphviz.
mlissner

2
Điều này đòi hỏi một chút công việc để cài đặt ở đây là 3 bước để trợ giúp. 1. Cài đặt qua pip, 2. Cài đặt GraphViz qua exe 3. Thiết lập các biến đường dẫn vào thư mục GraphViz 4. Tìm hiểu cách khắc phục tất cả các lỗi khác. 5. Tìm ra nơi nó lưu tập tin png?
đầm lầy

199

Thật đáng để chỉ ra rằng việc sử dụng trình lược tả chỉ hoạt động (theo mặc định) trên luồng chính và bạn sẽ không nhận được bất kỳ thông tin nào từ các luồng khác nếu bạn sử dụng chúng. Điều này có thể là một chút của một gotcha vì nó hoàn toàn không được đề cập trong tài liệu hồ sơ .

Nếu bạn cũng muốn lập hồ sơ chủ đề, bạn sẽ muốn xem threading.setprofile()chức năng trong tài liệu.

Bạn cũng có thể tạo threading.Threadlớp con của riêng bạn để làm điều đó:

class ProfiledThread(threading.Thread):
    # Overrides threading.Thread.run()
    def run(self):
        profiler = cProfile.Profile()
        try:
            return profiler.runcall(threading.Thread.run, self)
        finally:
            profiler.dump_stats('myprofile-%d.profile' % (self.ident,))

và sử dụng ProfiledThreadlớp đó thay vì lớp tiêu chuẩn. Nó có thể giúp bạn linh hoạt hơn, nhưng tôi không chắc nó có giá trị hay không, đặc biệt nếu bạn đang sử dụng mã của bên thứ ba sẽ không sử dụng lớp của bạn.


1
Tôi cũng không thấy bất kỳ tài liệu tham khảo nào về runcall trong tài liệu này. Nhìn vào cProfile.py, tôi không chắc tại sao bạn sử dụng hàm threading.Thread.run cũng không tự làm đối số. Tôi đã dự kiến ​​sẽ thấy một tài liệu tham khảo cho phương pháp chạy của luồng khác ở đây.
PypeBros

Nó không có trong tài liệu, nhưng nó nằm trong mô-đun. Xem hg.python.org/cpython/file/6bf07db23445/Lib/cProfile.py#l140 . Điều đó cho phép bạn cấu hình một cuộc gọi chức năng cụ thể và trong trường hợp của chúng tôi, chúng tôi muốn cấu hình targetchức năng của Thread , đó là những gì threading.Thread.run()cuộc gọi thực hiện. Nhưng như tôi đã nói trong câu trả lời, có lẽ không đáng để phân lớp Thread, vì bất kỳ mã bên thứ ba nào cũng sẽ không sử dụng nó và thay vào đó sử dụng threading.setprofile().
Joe Shaw

9
gói mã với profiler.enable () và profiler.disable () dường như cũng hoạt động khá tốt. Về cơ bản đó là những gì runcall làm và nó không thực thi bất kỳ số lượng đối số hoặc những điều tương tự.
PypeBros

1
Tôi đã kết hợp stackoverflow.com/questions/10748118/ của riêng tôi với ddaa.net/blog/python/lsprof-calltree và nó hoạt động tốt ;! -)
Dima Tisnek

1
Joe, bạn có biết cách trình hồ sơ chơi với asyncio trong Python 3.4 không?
Nick Chammas

149

Wiki python là một trang tuyệt vời để định hình tài nguyên: http://wiki.python.org/moin/PythonSpeed/PerformanceTips#Profiling_Code

cũng như các tài liệu python: http://docs.python.org/l Library / profile.html

như thể hiện bởi Chris Lawlor cProfile là một công cụ tuyệt vời và có thể dễ dàng được sử dụng để in ra màn hình:

python -m cProfile -s time mine.py <args>

hoặc để tập tin:

python -m cProfile -o output.file mine.py <args>

PS> Nếu bạn đang sử dụng Ubuntu, hãy đảm bảo cài đặt cấu hình python

apt-get install python-profiler 

Nếu bạn xuất ra tệp, bạn có thể có được hình ảnh đẹp bằng cách sử dụng các công cụ sau

PyCallGraph: một công cụ để tạo
cài đặt hình ảnh biểu đồ cuộc gọi :

 pip install pycallgraph

chạy:

 pycallgraph mine.py args

lượt xem:

 gimp pycallgraph.png

Bạn có thể sử dụng bất cứ thứ gì bạn muốn để xem tệp png, tôi đã sử dụng gimp
Thật không may tôi thường nhận được

dot: đồ thị quá lớn đối với bitmap cairo-renderer. Chia tỷ lệ 0,257079 cho phù hợp

mà làm cho hình ảnh của tôi nhỏ bất thường. Vì vậy, tôi thường tạo các tập tin svg:

pycallgraph -f svg -o pycallgraph.svg mine.py <args>

PS> đảm bảo cài đặt graphviz (cung cấp chương trình dấu chấm):

pip install graphviz

Vẽ đồ thị thay thế bằng gprof2dot qua @max / @quodlibetor:

pip install gprof2dot
python -m cProfile -o profile.pstats mine.py
gprof2dot -f pstats profile.pstats | dot -Tsvg -o mine.svg

12
gprof2dot cũng có thể làm những biểu đồ đó. Tôi nghĩ rằng đầu ra là một chút đẹp hơn ( ví dụ ).
MAXY

2
graphviz cũng được yêu cầu nếu bạn đang sử dụng OSX
Vaibhav Mishra

134

Nhận xét của @ Maxy về câu trả lời này đã giúp tôi đủ để tôi nghĩ rằng nó xứng đáng với câu trả lời của riêng mình: Tôi đã có các tệp .pstats do cProfile tạo và tôi không muốn chạy lại mọi thứ với pycallgraph, vì vậy tôi đã sử dụng gprof2dot và đã khá svss:

$ sudo apt-get install graphviz
$ git clone https://github.com/jrfonseca/gprof2dot
$ ln -s "$PWD"/gprof2dot/gprof2dot.py ~/bin
$ cd $PROJECT_DIR
$ gprof2dot.py -f pstats profile.pstats | dot -Tsvg -o callgraph.svg

và BLAM!

Nó sử dụng dấu chấm (điều tương tự mà pycallgraph sử dụng) để đầu ra trông tương tự nhau. Tôi có ấn tượng rằng gprof2dot mất ít thông tin hơn:

đầu ra ví dụ gprof2dot


1
Cách tiếp cận tốt, hoạt động thực sự tốt khi bạn có thể xem SVG trong Chrome, v.v. và tăng tỷ lệ lên / xuống. Dòng thứ ba có lỗi đánh máy, nên là: ln -s pwd/gprof2dot/gprof2dot.py $ HOME / bin (hoặc sử dụng ln -s $ PWD / gprof2dot / gprof2dot.py ~ / bin trong hầu hết các shell - dấu trọng âm được lấy làm định dạng đầu tiên phiên bản).
RichVel

2
Ah, điểm tốt. Tôi nhận được lnthứ tự lập luận sai gần như mọi lúc.
quodlibetor

7
mẹo nhỏ là hãy nhớ rằng ln và cp có cùng thứ tự đối số - hãy nghĩ về nó như 'sao chép
tệp1 vào tệp2

Điều đó có ý nghĩa, tôi nghĩ rằng việc sử dụng "MỤC TIÊU" trong trang web ném cho tôi.
quodlibetor

Xin vui lòng, làm thế nào bạn có được góc tròn? Tôi cảm thấy nó cải thiện khả năng đọc. Tôi chỉ nhận được những góc nhọn xấu xí mà không mát mẻ khi có nhiều cạnh xung quanh hộp.
Hibou57

78

Tôi tình cờ gặp một công cụ tiện dụng có tên SnakeViz khi nghiên cứu chủ đề này. SnakeViz là một công cụ trực quan hóa hồ sơ dựa trên web. Nó rất dễ dàng để cài đặt và sử dụng. Cách thông thường tôi sử dụng là tạo tệp stat %prunvà sau đó thực hiện phân tích trong SnakeViz.

Kỹ thuật viz chính được sử dụng là biểu đồ Sunburst như hình bên dưới, trong đó hệ thống phân cấp của các lệnh gọi hàm được sắp xếp thành các lớp của cung và thông tin thời gian được mã hóa theo chiều rộng góc của chúng.

Điều tốt nhất là bạn có thể tương tác với biểu đồ. Ví dụ, để phóng to người ta có thể nhấp vào một vòng cung, và vòng cung và hậu duệ của nó sẽ được phóng to như một tia nắng mới để hiển thị thêm chi tiết.

nhập mô tả hình ảnh ở đây


1
Câu trả lời của CodeCabbie bao gồm các hướng dẫn cài đặt (ngắn) và cho biết cách (dễ dàng) sử dụng SnakeViz.
Oren Milman

Ở đây tôi đã đọc IMHO hướng dẫn tốt cách sử dụng hồ sơ cho Python trên máy tính xách tay jupyter
Alexei Martianov

73

Đơn giảnnhanh nhất để tìm nơi mọi lúc mọi nơi.

1. pip install snakeviz

2. python -m cProfile -o temp.dat <PROGRAM>.py

3. snakeviz temp.dat

Vẽ biểu đồ hình tròn trong trình duyệt. Phần lớn nhất là chức năng vấn đề. Rất đơn giản.


1
Điều này rất hữu ích. Cảm ơn bạn.
jippyjoe4

55

Tôi nghĩ rằng đó cProfilelà tuyệt vời cho hồ sơ, trong khi đó kcachegrindlà tuyệt vời để hình dung kết quả. Ở pyprof2calltreegiữa xử lý chuyển đổi tập tin.

python -m cProfile -o script.profile script.py
pyprof2calltree -i script.profile -o script.calltree
kcachegrind script.calltree

Để cài đặt các công cụ cần thiết (ít nhất là trên Ubuntu):

apt-get install kcachegrind
pip install pyprof2calltree

Kết quả:

Ảnh chụp màn hình kết quả


9
Người dùng Mac cài đặt brew install qcachegrindvà substitude từng kcachegrindqcachegrind trong mô tả cho hồ sơ thành công.
Kevin Katzke

Tôi đã phải làm điều này để khiến nó hoạt động:export QT_X11_NO_MITSHM=1
Yonatan Simson

41

Một điều đáng nói nữa là trình xem kết xuất GUI cProfile RunSnakeRun . Nó cho phép bạn sắp xếp và chọn, từ đó phóng to các phần có liên quan của chương trình. Kích thước của các hình chữ nhật trong hình tỷ lệ thuận với thời gian thực hiện. Nếu bạn di chuột qua một hình chữ nhật, nó sẽ làm nổi bật cuộc gọi trong bảng và ở mọi nơi trên bản đồ. Khi bạn bấm đúp vào hình chữ nhật, nó sẽ phóng to phần đó. Nó sẽ cho bạn biết ai gọi phần đó và phần đó gọi là gì.

Các thông tin mô tả là rất hữu ích. Nó cho bạn thấy mã cho bit đó có thể hữu ích khi bạn xử lý các cuộc gọi thư viện tích hợp. Nó cho bạn biết tập tin nào và dòng nào để tìm mã.

Cũng muốn chỉ ra rằng OP nói 'hồ sơ' nhưng có vẻ như anh ta có nghĩa là 'thời gian'. Hãy ghi nhớ các chương trình sẽ chạy chậm hơn khi được định hình.

nhập mô tả hình ảnh ở đây


34

Một mô-đun cấu hình đẹp là line_profiler (được gọi bằng cách sử dụng tập lệnh kernprof.py). Nó có thể được tải xuống ở đây .

Hiểu biết của tôi là cProfile chỉ cung cấp thông tin về tổng thời gian dành cho mỗi chức năng. Vì vậy, các dòng mã riêng lẻ không được tính thời gian. Đây là một vấn đề trong điện toán khoa học vì thường một dòng duy nhất có thể mất rất nhiều thời gian. Ngoài ra, như tôi nhớ, cProfile đã không nắm bắt được thời gian tôi dành để nói numpy.dot.


34

Gần đây tôi đã tạo ra cá ngừ để trực quan hóa thời gian chạy và nhập hồ sơ Python; Điều này có thể hữu ích ở đây.

nhập mô tả hình ảnh ở đây

Cài đặt với

pip install tuna

Tạo một hồ sơ thời gian chạy

python3 -m cProfile -o program.prof yourfile.py

hoặc một hồ sơ nhập khẩu (yêu cầu Python 3.7+)

python3 -X importprofile yourfile.py 2> import.log

Sau đó chỉ cần chạy cá ngừ trên tập tin

tuna program.prof

33

pprofile

line_profiler(đã được trình bày ở đây) cũng được truyền cảm hứng pprofile, được mô tả là:

Line-granularity, profiler nhận dạng xác định và thống kê tinh khiết python

Nó cung cấp độ chi tiết dòng như line_profilerlà Python thuần túy, có thể được sử dụng như một lệnh độc lập hoặc mô-đun và thậm chí có thể tạo các tệp định dạng callgrind có thể dễ dàng phân tích [k|q]cachegrind.

vprof

Ngoài ra còn có vprof , một gói Python được mô tả là:

[...] cung cấp trực quan hóa phong phú và tương tác cho các đặc điểm khác nhau của chương trình Python như thời gian chạy và sử dụng bộ nhớ.

bản đồ nhiệt


14

Có rất nhiều câu trả lời hay nhưng họ sử dụng dòng lệnh hoặc một số chương trình bên ngoài để định hình và / hoặc sắp xếp kết quả.

Tôi thực sự đã bỏ lỡ một số cách tôi có thể sử dụng trong IDE của mình (nhật thực-PyDev) mà không cần chạm vào dòng lệnh hoặc cài đặt bất cứ thứ gì. Vì vậy, đây là.

Hồ sơ không có dòng lệnh

def count():
    from math import sqrt
    for x in range(10**5):
        sqrt(x)

if __name__ == '__main__':
    import cProfile, pstats
    cProfile.run("count()", "{}.profile".format(__file__))
    s = pstats.Stats("{}.profile".format(__file__))
    s.strip_dirs()
    s.sort_stats("time").print_stats(10)

Xem tài liệu hoặc câu trả lời khác để biết thêm.


ví dụ: hồ sơ in {map} hoặc {xxx}. Làm thế nào để tôi biết phương thức {xxx} được gọi từ tệp nào? hồ sơ của tôi in {phương thức 'nén' các đối tượng 'zlib.Compress'} mất phần lớn thời gian, nhưng tôi không sử dụng bất kỳ zlib nào, vì vậy tôi đoán một số chức năng gọi numpy có thể sử dụng nó. Làm thế nào để tôi biết tập tin và dòng chính xác nào mất nhiều thời gian?
machen

12

Theo câu trả lời của Joe Shaw về mã đa luồng không hoạt động như mong đợi, tôi đoán rằng runcallphương thức trong cProfile chỉ đơn thuần là làm self.enable()self.disable() gọi xung quanh lệnh gọi được lược tả, vì vậy bạn có thể tự mình thực hiện và có bất kỳ mã nào bạn muốn ở giữa can thiệp tối thiểu với mã hiện có.


3
Mẹo tuyệt vời! Xem nhanh cprofile.pymã nguồn của nó cho thấy chính xác đó là gì runcall(). Cụ thể hơn, sau khi tạo một cá thể Hồ sơ với prof = cprofile.Profile(), hãy gọi ngay lập tức prof.disable(), sau đó chỉ cần thêm prof.enable()prof.disable()gọi xung quanh phần mã bạn muốn định hình.
martineau

Điều này rất hữu ích, nhưng có vẻ như mã thực sự giữa bật và tắt không được định hình - chỉ các chức năng mà nó gọi. Tôi có quyền này không? Tôi phải bọc mã đó trong một hàm gọi cho nó để đếm bất kỳ số nào trong print_stats ().
Bob Stein

10

Trong nguồn của Virtaal, có một lớp và trình trang trí rất hữu ích có thể làm cho việc định hình (ngay cả đối với các phương thức / hàm cụ thể) rất dễ dàng. Đầu ra sau đó có thể được xem rất thoải mái trong KCacheGrind.


1
Cảm ơn bạn cho viên ngọc này. FYI: Điều này có thể được sử dụng như một mô-đun độc lập với bất kỳ mã nào, cơ sở mã Virtaal là không bắt buộc. Chỉ cần lưu tệp vào profiling.py và nhập profile_func (). Sử dụng @profile_func () làm công cụ trang trí cho bất kỳ chức năng nào bạn cần để cấu hình và viola. :)
Amjith

9

cProfile là tuyệt vời để lập hồ sơ nhanh chóng nhưng hầu hết thời gian nó đã kết thúc với tôi với các lỗi. Hàm runcx giải quyết vấn đề này bằng cách khởi tạo chính xác môi trường và các biến, hy vọng nó có thể hữu ích cho ai đó:

import cProfile
cProfile.runctx('foo()', None, locals())

7

Nếu bạn muốn tạo một hồ sơ tích lũy, nghĩa là chạy hàm nhiều lần liên tiếp và xem tổng kết quả.

bạn có thể sử dụng cumulative_profilertrang trí này :

đó là python> = 3.6 cụ thể, nhưng bạn có thể loại bỏ nonlocalđể nó hoạt động trên các phiên bản cũ hơn.

import cProfile, pstats

class _ProfileFunc:
    def __init__(self, func, sort_stats_by):
        self.func =  func
        self.profile_runs = []
        self.sort_stats_by = sort_stats_by

    def __call__(self, *args, **kwargs):
        pr = cProfile.Profile()
        pr.enable()  # this is the profiling section
        retval = self.func(*args, **kwargs)
        pr.disable()

        self.profile_runs.append(pr)
        ps = pstats.Stats(*self.profile_runs).sort_stats(self.sort_stats_by)
        return retval, ps

def cumulative_profiler(amount_of_times, sort_stats_by='time'):
    def real_decorator(function):
        def wrapper(*args, **kwargs):
            nonlocal function, amount_of_times, sort_stats_by  # for python 2.x remove this row

            profiled_func = _ProfileFunc(function, sort_stats_by)
            for i in range(amount_of_times):
                retval, ps = profiled_func(*args, **kwargs)
            ps.print_stats()
            return retval  # returns the results of the function
        return wrapper

    if callable(amount_of_times):  # incase you don't want to specify the amount of times
        func = amount_of_times  # amount_of_times is the function in here
        amount_of_times = 5  # the default amount
        return real_decorator(func)
    return real_decorator

Thí dụ

định hình hàm baz

import time

@cumulative_profiler
def baz():
    time.sleep(1)
    time.sleep(2)
    return 1

baz()

baz chạy 5 lần và in cái này:

         20 function calls in 15.003 seconds

   Ordered by: internal time

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
       10   15.003    1.500   15.003    1.500 {built-in method time.sleep}
        5    0.000    0.000   15.003    3.001 <ipython-input-9-c89afe010372>:3(baz)
        5    0.000    0.000    0.000    0.000 {method 'disable' of '_lsprof.Profiler' objects}

chỉ định số lần

@cumulative_profiler(3)
def baz():
    ...

7

Giải pháp chỉ dành cho thiết bị đầu cuối (và đơn giản nhất), trong trường hợp tất cả các giao diện người dùng ưa thích đó không cài đặt hoặc chạy:
bỏ qua cProfilehoàn toàn và thay thế bằng pyinstrument, sẽ thu thập và hiển thị cây cuộc gọi ngay sau khi thực hiện.

Tải về:

$ pip install pyinstrument

Hồ sơ và kết quả hiển thị:

$ python -m pyinstrument ./prog.py

Hoạt động với python2 và 3.

[EDIT] Tài liệu về API, chỉ để định hình một phần của mã, có thể được tìm thấy ở đây .


6

Cách của tôi là sử dụng yappi ( https://github.com/sumerc/yappi ). Nó đặc biệt hữu ích khi kết hợp với máy chủ RPC trong đó (thậm chí chỉ để gỡ lỗi) bạn đăng ký phương thức để bắt đầu, dừng và in thông tin hồ sơ, ví dụ: theo cách này:

@staticmethod
def startProfiler():
    yappi.start()

@staticmethod
def stopProfiler():
    yappi.stop()

@staticmethod
def printProfiler():
    stats = yappi.get_stats(yappi.SORTTYPE_TTOT, yappi.SORTORDER_DESC, 20)
    statPrint = '\n'
    namesArr = [len(str(stat[0])) for stat in stats.func_stats]
    log.debug("namesArr %s", str(namesArr))
    maxNameLen = max(namesArr)
    log.debug("maxNameLen: %s", maxNameLen)

    for stat in stats.func_stats:
        nameAppendSpaces = [' ' for i in range(maxNameLen - len(stat[0]))]
        log.debug('nameAppendSpaces: %s', nameAppendSpaces)
        blankSpace = ''
        for space in nameAppendSpaces:
            blankSpace += space

        log.debug("adding spaces: %s", len(nameAppendSpaces))
        statPrint = statPrint + str(stat[0]) + blankSpace + " " + str(stat[1]).ljust(8) + "\t" + str(
            round(stat[2], 2)).ljust(8 - len(str(stat[2]))) + "\t" + str(round(stat[3], 2)) + "\n"

    log.log(1000, "\nname" + ''.ljust(maxNameLen - 4) + " ncall \tttot \ttsub")
    log.log(1000, statPrint)

Sau đó, khi chương trình của bạn hoạt động, bạn có thể khởi động profiler bất cứ lúc nào bằng cách gọi startProfilerphương thức RPC và chuyển thông tin lược tả vào tệp nhật ký bằng cách gọi printProfiler(hoặc sửa đổi phương thức rpc để trả lại cho người gọi) và nhận đầu ra như vậy:

2014-02-19 16:32:24,128-|SVR-MAIN  |-(Thread-3   )-Level 1000: 
name                                                                                                                                      ncall     ttot    tsub
2014-02-19 16:32:24,128-|SVR-MAIN  |-(Thread-3   )-Level 1000: 
C:\Python27\lib\sched.py.run:80                                                                                                           22        0.11    0.05
M:\02_documents\_repos\09_aheadRepos\apps\ahdModbusSrv\pyAheadRpcSrv\xmlRpc.py.iterFnc:293                                                22        0.11    0.0
M:\02_documents\_repos\09_aheadRepos\apps\ahdModbusSrv\serverMain.py.makeIteration:515                                                    22        0.11    0.0
M:\02_documents\_repos\09_aheadRepos\apps\ahdModbusSrv\pyAheadRpcSrv\PicklingXMLRPC.py._dispatch:66                                       1         0.0     0.0
C:\Python27\lib\BaseHTTPServer.py.date_time_string:464                                                                                    1         0.0     0.0
c:\users\zasiec~1\appdata\local\temp\easy_install-hwcsr1\psutil-1.1.2-py2.7-win32.egg.tmp\psutil\_psmswindows.py._get_raw_meminfo:243     4         0.0     0.0
C:\Python27\lib\SimpleXMLRPCServer.py.decode_request_content:537                                                                          1         0.0     0.0
c:\users\zasiec~1\appdata\local\temp\easy_install-hwcsr1\psutil-1.1.2-py2.7-win32.egg.tmp\psutil\_psmswindows.py.get_system_cpu_times:148 4         0.0     0.0
<string>.__new__:8                                                                                                                        220       0.0     0.0
C:\Python27\lib\socket.py.close:276                                                                                                       4         0.0     0.0
C:\Python27\lib\threading.py.__init__:558                                                                                                 1         0.0     0.0
<string>.__new__:8                                                                                                                        4         0.0     0.0
C:\Python27\lib\threading.py.notify:372                                                                                                   1         0.0     0.0
C:\Python27\lib\rfc822.py.getheader:285                                                                                                   4         0.0     0.0
C:\Python27\lib\BaseHTTPServer.py.handle_one_request:301                                                                                  1         0.0     0.0
C:\Python27\lib\xmlrpclib.py.end:816                                                                                                      3         0.0     0.0
C:\Python27\lib\SimpleXMLRPCServer.py.do_POST:467                                                                                         1         0.0     0.0
C:\Python27\lib\SimpleXMLRPCServer.py.is_rpc_path_valid:460                                                                               1         0.0     0.0
C:\Python27\lib\SocketServer.py.close_request:475                                                                                         1         0.0     0.0
c:\users\zasiec~1\appdata\local\temp\easy_install-hwcsr1\psutil-1.1.2-py2.7-win32.egg.tmp\psutil\__init__.py.cpu_times:1066               4         0.0     0.0 

Nó có thể không hữu ích cho các tập lệnh ngắn nhưng giúp tối ưu hóa các quy trình loại máy chủ, đặc biệt là printProfilerphương thức được cung cấp có thể được gọi nhiều lần theo thời gian để lập hồ sơ và so sánh, ví dụ như các kịch bản sử dụng chương trình khác nhau.

Trong các phiên bản mới hơn của yappi, đoạn mã sau sẽ hoạt động:

@staticmethod
def printProfile():
    yappi.get_func_stats().print_all()

Không phải nó nên được đặt tên là Yappi Stupendous sao?
Therealstubot

Thật không may, đoạn mã trên chỉ hoạt động với phiên bản 0.62 không có sẵn trên pypy. Nhu cầu mô-đun được biên dịch từ 0,62 nguồn có sẵn ở đây: github.com/nirs/yappi/releases hoặc sử dụng xây dựng tôi đã làm cho các cửa sổ trong repo chia hai cho rằng mục đích github.com/Girgitt/yappi/releases
Ông Girgitt

có thể dễ dàng cung cấp khả năng tương thích với phiên bản 1.0 - ít nhất là cho đầu ra in - bằng cách sửa đổi chức năng printProfiler: def printProfiler(): if not yappi_available: return stats = yappi.get_func_stats() stats.print_all(columns={0:("name",90), 1:("ncall", 5), 2:("tsub", 8), 3:("ttot", 8), 4:("tavg",8)}) (OK sau khi thử vài lần để chèn khối mã vào nhận xét tôi đã từ bỏ. Điều này thật khó tin đối với trang web Hỏi & Đáp theo định hướng lập trình. )
Ông Girgitt

4

Một công cụ mới để xử lý hồ sơ trong Python là PyVmMonitor: http://www.pyvmmonitor.com/

Nó có một số tính năng độc đáo như

  • Đính kèm profiler vào chương trình đang chạy (CPython)
  • Hồ sơ theo yêu cầu với tích hợp Yappi
  • Hồ sơ trên một máy khác
  • Hỗ trợ nhiều quy trình (đa xử lý, django ...)
  • Xem mẫu trực tiếp / CPU (với lựa chọn phạm vi thời gian)
  • Hồ sơ xác định thông qua tích hợp cProfile / hồ sơ
  • Phân tích kết quả PStats hiện có
  • Mở tệp DOT
  • Truy cập API có lập trình
  • Nhóm mẫu theo phương pháp hoặc dòng
  • Tích hợp PyDev
  • Tích hợp PyCharm

Lưu ý: đó là thương mại, nhưng miễn phí cho nguồn mở.


4

gprof2dot_magic

Hàm ma thuật gprof2dotđể cấu hình bất kỳ câu lệnh Python nào dưới dạng biểu đồ DOT trong JupyterLab hoặc Jupyter Notebook.

nhập mô tả hình ảnh ở đây

Repit GitHub: https://github.com/mattijn/gprof2dot_magic

cài đặt

Hãy chắc chắn rằng bạn đã có gói Python gprof2dot_magic.

pip install gprof2dot_magic

Nó phụ thuộc gprof2dotgraphvizcũng sẽ được cài đặt

sử dụng

Để kích hoạt chức năng ma thuật, trước tiên hãy tải gprof2dot_magicmô-đun

%load_ext gprof2dot_magic

và sau đó cấu hình bất kỳ câu lệnh nào dưới dạng biểu đồ DOT như sau:

%gprof2dot print('hello world')

nhập mô tả hình ảnh ở đây


3

Bạn đã bao giờ muốn biết kịch bản python đang làm cái quái gì chưa? Nhập vỏ Kiểm tra. Inspect Shell cho phép bạn in / thay đổi toàn cầu và chạy các chức năng mà không làm gián đoạn tập lệnh đang chạy. Bây giờ với tự động hoàn thành và lịch sử lệnh (chỉ trên linux).

Inspect Shell không phải là trình gỡ lỗi kiểu pdb.

https://github.com/amoffat/Inspect-Shell

Bạn có thể sử dụng nó (và đồng hồ đeo tay của bạn).


3

Để thêm vào https://stackoverflow.com/a/582337/1070617 ,

Tôi đã viết mô-đun này cho phép bạn sử dụng cProfile và xem đầu ra của nó một cách dễ dàng. Thêm tại đây: https://github.com/ymichael/cprofilev

$ python -m cprofilev /your/python/program
# Go to http://localhost:4000 to view collected statistics.

Xem thêm: http://ymichael.com/2014/03/08/profiling-python-with-cprofile.html về cách hiểu ý nghĩa của số liệu thống kê được thu thập.


3

Nó sẽ phụ thuộc vào những gì bạn muốn xem trong hồ sơ. Số liệu thời gian đơn giản có thể được đưa ra bởi (bash).

time python python_prog.py

Ngay cả '/ usr / bin / time' cũng có thể xuất số liệu chi tiết bằng cách sử dụng cờ '--verbose'.

Để kiểm tra số liệu thời gian được cung cấp bởi mỗi chức năng và để hiểu rõ hơn về thời gian dành cho các chức năng, bạn có thể sử dụng cProfile sẵn có trong python.

Đi sâu vào các số liệu chi tiết hơn như hiệu suất, thời gian không phải là số liệu duy nhất. Bạn có thể lo lắng về bộ nhớ, chủ đề, v.v.
Tùy chọn cấu hình:
1. line_profiler là một trình lược tả khác được sử dụng phổ biến để tìm ra số liệu thời gian theo từng dòng.
2. memory_profiler là một công cụ để cấu hình sử dụng bộ nhớ.
3. heapy (từ dự án Guppy) Hồ sơ cách các đối tượng trong heap được sử dụng.

Đây là một số trong những phổ biến tôi có xu hướng sử dụng. Nhưng nếu bạn muốn tìm hiểu thêm, hãy thử đọc cuốn sách này Đó là một cuốn sách khá hay về khởi đầu với hiệu suất trong tâm trí. Bạn có thể chuyển sang các chủ đề nâng cao bằng cách sử dụng python và JIT (Just-in-time) python đã biên dịch.


2

Với trình lược tả thống kê như austin , không yêu cầu thiết bị nào, có nghĩa là bạn có thể lấy dữ liệu lược tả từ ứng dụng Python chỉ bằng

austin python3 my_script.py

Đầu ra thô không hữu ích lắm, nhưng bạn có thể chuyển nó sang firegraph.pl để có được biểu diễn biểu đồ ngọn lửa của dữ liệu đó cung cấp cho bạn thông tin về thời gian (tính bằng micro giây của thời gian thực) đang được sử dụng.

austin python3 my_script.py | flamegraph.pl > my_script_profile.svg

2

Để có được số liệu thống kê hồ sơ nhanh chóng trên một máy tính xách tay IPython. Người ta có thể nhúng line_profilermemory_profiler thẳng vào sổ ghi chép của họ.

Một gói hữu ích khác là Pympler . Nó là một gói hồ sơ mạnh mẽ có khả năng theo dõi các lớp, đối tượng, chức năng, rò rỉ bộ nhớ, v.v. Ví dụ bên dưới, Tài liệu đính kèm.

Hiểu rồi!

!pip install line_profiler
!pip install memory_profiler
!pip install pympler

Tải nó!

%load_ext line_profiler
%load_ext memory_profiler

Sử dụng nó!


%thời gian

%time print('Outputs CPU time,Wall Clock time') 
#CPU times: user 2 µs, sys: 0 ns, total: 2 µs Wall time: 5.96 µs

Cung cấp:

  • Thời gian CPU: Thời gian thực hiện cấp CPU
  • sys lần: thời gian thực hiện cấp hệ thống
  • tổng cộng: thời gian CPU + thời gian hệ thống
  • Giờ tường: Giờ đồng hồ treo tường

% thời gian

%timeit -r 7 -n 1000 print('Outputs execution time of the snippet') 
#1000 loops, best of 7: 7.46 ns per loop
  • Cung cấp thời gian tốt nhất trong số lần chạy (r) đã cho trong vòng lặp (n) lần.
  • Đầu ra chi tiết về bộ nhớ đệm hệ thống:
    • Khi các đoạn mã được thực thi nhiều lần, hệ thống sẽ lưu trữ một số ý kiến ​​và không thực hiện lại chúng có thể cản trở tính chính xác của các báo cáo hồ sơ.

% cắt tỉa

%prun -s cumulative 'Code to profile' 

Cung cấp:

  • số lượng cuộc gọi chức năng (ncalls)
  • có các mục trên mỗi lệnh gọi hàm (riêng biệt)
  • thời gian thực hiện mỗi cuộc gọi (percall)
  • thời gian trôi qua cho đến khi chức năng đó gọi (cumtime)
  • tên của func / module gọi là ...

Hồ sơ tích lũy


% ghi nhớ

%memit 'Code to profile'
#peak memory: 199.45 MiB, increment: 0.00 MiB

Cung cấp:

  • Sử dụng bộ nhớ

% lprun

#Example function
def fun():
  for i in range(10):
    print(i)

#Usage: %lprun <name_of_the_function> function
%lprun -f fun fun()

Cung cấp:

  • Dòng thống kê khôn ngoan

LineProfile


sys.getsizeof

sys.getsizeof('code to profile')
# 64 bytes

Trả về kích thước của một đối tượng tính bằng byte.


asizeof () từ pympler

from pympler import asizeof
obj = [1,2,("hey","ha"),3]
print(asizeof.asizeof(obj,stats=4))

pympler.asizeof có thể được sử dụng để điều tra lượng bộ nhớ mà một số đối tượng Python tiêu thụ. Ngược lại với sys.getsizeof, các đối tượng kích thước asizeof đệ quy

pympler.asizeof


theo dõi từ pympler

from pympler import tracker
tr = tracker.SummaryTracker()
def fun():
  li = [1,2,3]
  di = {"ha":"haha","duh":"Umm"}
fun()
tr.print_diff()

Theo dõi tuổi thọ của một chức năng.

đầu ra theo dõi

Gói Pympler bao gồm một số lượng lớn các hàm tiện ích cao cho mã hồ sơ. Tất cả trong số đó không thể được bảo hiểm ở đây. Xem tài liệu đính kèm để thực hiện hồ sơ dài dòng.

Tài liệu Pympler


1

Ngoài ra còn có một hồ sơ thống kê được gọi là statprof . Đây là một trình lược tả lấy mẫu, do đó, nó thêm chi phí tối thiểu vào mã của bạn và đưa ra các định thời dựa trên dòng (không chỉ dựa trên chức năng). Nó phù hợp hơn với các ứng dụng thời gian thực mềm như trò chơi, nhưng có thể có độ chính xác thấp hơn cProfile.

Các phiên bản trong pypi là một chút cũ, vì vậy có thể cài đặt nó với pipbằng cách xác định các kho git :

pip install git+git://github.com/bos/statprof.py@1a33eba91899afe17a8b752c6dfdec6f05dd0c01

Bạn có thể chạy nó như thế này:

import statprof

with statprof.profile():
    my_questionable_function()

Xem thêm https://stackoverflow.com/a/10333592/320036


1

Tôi vừa phát triển trình tạo hồ sơ của riêng mình lấy cảm hứng từ pypref_time:

https://github.com/modaresimr/auto_profiler

Bằng cách thêm một trình trang trí, nó sẽ hiển thị một cây các chức năng tốn thời gian

@Profiler(depth=4, on_disable=show)

Install by: pip install auto_profiler

Thí dụ

import time # line number 1
import random

from auto_profiler import Profiler, Tree

def f1():
    mysleep(.6+random.random())

def mysleep(t):
    time.sleep(t)

def fact(i):
    f1()
    if(i==1):
        return 1
    return i*fact(i-1)


def show(p):
    print('Time   [Hits * PerHit] Function name [Called from] [Function Location]\n'+\
          '-----------------------------------------------------------------------')
    print(Tree(p.root, threshold=0.5))

@Profiler(depth=4, on_disable=show)
def main():
    for i in range(5):
        f1()

    fact(3)


if __name__ == '__main__':
    main()

Ví dụ đầu ra


Time   [Hits * PerHit] Function name [Called from] [function location]
-----------------------------------------------------------------------
8.974s [1 * 8.974]  main  [auto-profiler/profiler.py:267]  [/test/t2.py:30]
├── 5.954s [5 * 1.191]  f1  [/test/t2.py:34]  [/test/t2.py:14]
   └── 5.954s [5 * 1.191]  mysleep  [/test/t2.py:15]  [/test/t2.py:17]
       └── 5.954s [5 * 1.191]  <time.sleep>
|
|
|   # The rest is for the example recursive function call fact
└── 3.020s [1 * 3.020]  fact  [/test/t2.py:36]  [/test/t2.py:20]
    ├── 0.849s [1 * 0.849]  f1  [/test/t2.py:21]  [/test/t2.py:14]
       └── 0.849s [1 * 0.849]  mysleep  [/test/t2.py:15]  [/test/t2.py:17]
           └── 0.849s [1 * 0.849]  <time.sleep>
    └── 2.171s [1 * 2.171]  fact  [/test/t2.py:24]  [/test/t2.py:20]
        ├── 1.552s [1 * 1.552]  f1  [/test/t2.py:21]  [/test/t2.py:14]
           └── 1.552s [1 * 1.552]  mysleep  [/test/t2.py:15]  [/test/t2.py:17]
        └── 0.619s [1 * 0.619]  fact  [/test/t2.py:24]  [/test/t2.py:20]
            └── 0.619s [1 * 0.619]  f1  [/test/t2.py:21]  [/test/t2.py:14]

0

Khi tôi không root trên máy chủ, tôi sử dụng lsprofcalltree.py và chạy chương trình của tôi như thế này:

python lsprofcalltree.py -o callgrind.1 test.py

Sau đó, tôi có thể mở báo cáo với bất kỳ phần mềm tương thích với lệnh gọi nào, như qcachegrind

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.