2010-10-13 29 views
72

Tôi đã sử dụng cProfile để cấu hình mã của tôi và nó hoạt động rất tốt. Tôi cũng sử dụng gprof2dot.py để trực quan hóa kết quả (làm cho nó rõ ràng hơn một chút).Làm cách nào để tôi có thể lập cấu hình mã vạch python theo từng dòng?

Tuy nhiên, cProfile (và hầu hết các trình biên dịch python khác mà tôi đã thấy cho đến nay) dường như chỉ có cấu hình ở cấp hàm gọi. Điều này gây ra sự nhầm lẫn khi một số chức năng được gọi từ những nơi khác nhau - Tôi không biết liệu cuộc gọi số 1 hoặc cuộc gọi số 2 có chiếm phần lớn thời gian hay không. Điều này càng trở nên tồi tệ hơn khi hàm trong câu hỏi là 6 cấp độ sâu, được gọi từ 7 địa điểm khác.

Vì vậy, câu hỏi của tôi là: làm thế nào để tôi có được một hồ sơ theo dòng? Thay vì điều này:

function #12, total time: 2.0s 

Tôi muốn nhìn thấy một cái gì đó như thế này:

function #12 (called from somefile.py:102) 0.5s 
function #12 (called from main.py:12) 1.5s 

cProfile không hiển thị bao nhiêu trong tổng thời gian "chuyển" cho phụ huynh, nhưng một lần nữa kết nối này bị mất khi bạn có một loạt các lớp và các cuộc gọi được kết nối.

Lý tưởng nhất, tôi muốn có GUI có thể phân tích cú pháp thông qua dữ liệu, sau đó hiển thị cho tôi tệp nguồn của tôi với tổng thời gian cho mỗi dòng. Một cái gì đó như thế này:

main.py: 

a = 1 # 0.0s 
result = func(a) # 0.4s 
c = 1000 # 0.0s 
result = func(c) # 5.0s 

Sau đó, tôi muốn có thể click vào thứ hai "func (c)" gọi điện để xem những gì đang chiếm thời gian trong cuộc gọi đó, tách biệt với "func (a)" gọi.

Điều đó có hợp lý không? Có thư viện hồ sơ nào thu thập loại thông tin này không? Có một số công cụ tuyệt vời mà tôi đã bỏ lỡ? Mọi phản hồi đều được đánh giá cao. Cảm ơn!!

+2

Tôi đoán là bạn sẽ quan tâm đến 'pstats.print_callers'. Một ví dụ là [ở đây] (http://www.doughellmann.com/PyMOTW/profile/). –

+0

Muhammad, điều đó chắc chắn hữu ích! Ít nhất nó sửa chữa một vấn đề: tách các cuộc gọi chức năng tùy thuộc vào nguồn gốc. Tôi nghĩ câu trả lời của Joe Kington gần hơn với mục đích của tôi, nhưng print_callers() chắc chắn đã giúp tôi nửa chừng. Cảm ơn! – rocketmonkeys

Trả lời

84

Tôi tin rằng đó là những gì Robert Kern's line_profiler được thiết kế cho. Từ liên kết:

File: pystone.py 
Function: Proc2 at line 149 
Total time: 0.606656 s 

Line #  Hits   Time Per Hit % Time Line Contents 
============================================================== 
    149           @profile 
    150           def Proc2(IntParIO): 
    151  50000  82003  1.6  13.5  IntLoc = IntParIO + 10 
    152  50000  63162  1.3  10.4  while 1: 
    153  50000  69065  1.4  11.4   if Char1Glob == 'A': 
    154  50000  66354  1.3  10.9    IntLoc = IntLoc - 1 
    155  50000  67263  1.3  11.1    IntParIO = IntLoc - IntGlob 
    156  50000  65494  1.3  10.8    EnumLoc = Ident1 
    157  50000  68001  1.4  11.2   if EnumLoc == Ident1: 
    158  50000  63739  1.3  10.5    break 
    159  50000  61575  1.2  10.1  return IntParIO 

Hy vọng điều đó sẽ hữu ích!

+1

Joe, đây là * chính xác * những gì tôi đang tìm kiếm. Tôi chỉ có thể sử dụng một trình trang trí, đính kèm một đối tượng LineProfiler() vào một vài hàm, và nó sẽ nhổ ra một cấu hình theo từng dòng của hàm. Tôi thực sự muốn có một cách đồ họa để xem kết quả, nhưng đây là một khởi đầu tuyệt vời! Cảm ơn! – rocketmonkeys

+1

Là một followup: Tôi đã sử dụng này một vài lần, và tôi thậm chí đã thực hiện một trang trí django @profiler để tự động bọc xem trong dòng này-by-line profiler và nhổ ra các kết quả. Nó đã được hoàn hảo! Đây thực sự là những gì cần thiết khi tôi định hình một cái nhìn. Nó không thể chỉ cho tôi đệ quy những gì đang mất thời gian, nhưng tôi ít nhất có thể thu hẹp nó xuống một dòng. Đó thường là những gì tôi cần. Cảm ơn một lần nữa! – rocketmonkeys

+3

Line_profiler có hoạt động với Python 3 không? Tôi không thể nhận được bất kỳ thông tin về điều đó. – user1251007

19

Bạn cũng có thể sử dụng pprofile (pypi). Nếu bạn muốn cấu hình toàn bộ quá trình thực hiện, nó không yêu cầu sửa đổi mã nguồn. Bạn cũng có thể cấu hình một tập hợp con của một chương trình lớn hơn trong hai cách:

  • Toggle profiling khi đạt đến một điểm cụ thể trong các mã, chẳng hạn như:

    import pprofile 
    profiler = pprofile.Profile() 
    with profiler: 
        some_code 
    # Process profile content: generate a cachegrind file and send it to user. 
    
  • Toggle profiling không đồng bộ từ cuộc gọi stack (yêu cầu cách kích hoạt mã này trong ứng dụng được xem xét, ví dụ: trình xử lý tín hiệu hoặc chuỗi công nhân có sẵn) bằng cách sử dụng lược tả thống kê:

    import pprofile 
    profiler = pprofile.StatisticalProfile() 
    statistical_profiler_thread = pprofile.StatisticalThread(
        profiler=profiler, 
    ) 
    with statistical_profiler_thread: 
        sleep(n) 
    # Likewise, process profile content 
    

Mã định dạng đầu ra chú thích là giống như dòng profiler:

$ pprofile --threads 0 demo/threads.py 
Command line: ['demo/threads.py'] 
Total duration: 1.00573s 
File: demo/threads.py 
File duration: 1.00168s (99.60%) 
Line #|  Hits|   Time| Time per hit|  %|Source code 
------+----------+-------------+-------------+-------+----------- 
    1|   2| 3.21865e-05| 1.60933e-05| 0.00%|import threading 
    2|   1| 5.96046e-06| 5.96046e-06| 0.00%|import time 
    3|   0|   0|   0| 0.00%| 
    4|   2| 1.5974e-05| 7.98702e-06| 0.00%|def func(): 
    5|   1|  1.00111|  1.00111| 99.54%| time.sleep(1) 
    6|   0|   0|   0| 0.00%| 
    7|   2| 2.00272e-05| 1.00136e-05| 0.00%|def func2(): 
    8|   1| 1.69277e-05| 1.69277e-05| 0.00%| pass 
    9|   0|   0|   0| 0.00%| 
    10|   1| 1.81198e-05| 1.81198e-05| 0.00%|t1 = threading.Thread(target=func) 
(call)|   1| 0.000610828| 0.000610828| 0.06%|# /usr/lib/python2.7/threading.py:436 __init__ 
    11|   1| 1.52588e-05| 1.52588e-05| 0.00%|t2 = threading.Thread(target=func) 
(call)|   1| 0.000438929| 0.000438929| 0.04%|# /usr/lib/python2.7/threading.py:436 __init__ 
    12|   1| 4.79221e-05| 4.79221e-05| 0.00%|t1.start() 
(call)|   1| 0.000843048| 0.000843048| 0.08%|# /usr/lib/python2.7/threading.py:485 start 
    13|   1| 6.48499e-05| 6.48499e-05| 0.01%|t2.start() 
(call)|   1| 0.00115609| 0.00115609| 0.11%|# /usr/lib/python2.7/threading.py:485 start 
    14|   1| 0.000205994| 0.000205994| 0.02%|(func(), func2()) 
(call)|   1|  1.00112|  1.00112| 99.54%|# demo/threads.py:4 func 
(call)|   1| 3.09944e-05| 3.09944e-05| 0.00%|# demo/threads.py:7 func2 
    15|   1| 7.62939e-05| 7.62939e-05| 0.01%|t1.join() 
(call)|   1| 0.000423908| 0.000423908| 0.04%|# /usr/lib/python2.7/threading.py:653 join 
    16|   1| 5.26905e-05| 5.26905e-05| 0.01%|t2.join() 
(call)|   1| 0.000320196| 0.000320196| 0.03%|# /usr/lib/python2.7/threading.py:653 join 

Lưu ý rằng vì pprofile không dựa vào sửa đổi mã nó có thể cấu hình báo cáo mô-đun cấp cao nhất, cho phép để cấu hình thời gian chương trình khởi động (bao lâu cần nhập mô-đun, khởi tạo các hình cầu, ...).

Nó có thể tạo đầu ra được định dạng theo cachegrind, vì vậy bạn có thể sử dụng kcachegrind để duyệt các kết quả lớn dễ dàng.

Tiết lộ: Tôi là tác giả pprofile.

+0

+1 Cảm ơn sự đóng góp của bạn. Tôi có một quan điểm khác biệt nhỏ - việc đo lường thời gian bao gồm được thực hiện bởi các câu lệnh và hàm là một mục tiêu. Tìm hiểu những gì có thể được thực hiện để làm cho mã nhanh hơn là một mục tiêu khác. 10^6 dòng mã, mã có thể lãng phí phần lớn thời gian.Đường tôi tìm thấy nó bằng cách lấy một số lượng nhỏ các mẫu rất chi tiết, và kiểm tra chúng với mắt người - không tóm tắt. Vấn đề được phơi bày bởi phần thời gian nó lãng phí. –

+1

Bạn nói đúng, tôi không đề cập đến việc sử dụng pprofile khi người ta muốn lược tả một tập con nhỏ hơn. Tôi đã chỉnh sửa bài đăng của mình để thêm ví dụ về điều này. – vpelletier

+0

Đây chính xác là những gì tôi đang tìm kiếm: không xâm nhập và mở rộng. – egpbos

0

PyVmMonitor có chế độ xem trực tiếp có thể giúp bạn ở đó (bạn có thể kết nối với chương trình đang chạy và nhận thống kê từ chương trình đang chạy).

Xem: http://www.pyvmmonitor.com/

Các vấn đề liên quan