我如何找出我的代码的哪些部分在 Python 中效率低下
How do I find out what parts of my code are inefficient in Python
在上一个问题中,我问的是多处理,使用多核使程序 运行 更快,有人告诉我:
More often than not, you can get a 100x+ optimization with better code compared to a 4x improvement and additional complexities with multiprocessing
然后他们建议我应该:
Use a profiler to understand what is slow, then focus on optimizing that.
所以我去了这个问题:How can you profile a script?
在这里我找到了 cProfile
并将其实现到一些测试代码中以查看其工作原理。
这是我的代码:
import cProfile
def foo():
for i in range(10000):
a = i**i
if i % 1000 == 0:
print(i)
cProfile.run('foo()')
然而,在 运行 之后,这就是我得到的:
0
1000
2000
3000
4000
5000
6000
7000
8000
9000
1018 function calls in 20.773 seconds
Ordered by: standard name
ncalls tottime percall cumtime percall filename:lineno(function)
1 0.000 0.000 20.773 20.773 <string>:1(<module>)
147 0.000 0.000 0.000 0.000 rpc.py:150(debug)
21 0.000 0.000 0.050 0.002 rpc.py:213(remotecall)
21 0.000 0.000 0.002 0.000 rpc.py:223(asynccall)
21 0.000 0.000 0.048 0.002 rpc.py:243(asyncreturn)
21 0.000 0.000 0.000 0.000 rpc.py:249(decoderesponse)
21 0.000 0.000 0.048 0.002 rpc.py:287(getresponse)
21 0.000 0.000 0.000 0.000 rpc.py:295(_proxify)
21 0.001 0.000 0.048 0.002 rpc.py:303(_getresponse)
21 0.000 0.000 0.000 0.000 rpc.py:325(newseq)
21 0.000 0.000 0.002 0.000 rpc.py:329(putmessage)
21 0.000 0.000 0.000 0.000 rpc.py:55(dumps)
20 0.000 0.000 0.001 0.000 rpc.py:556(__getattr__)
1 0.000 0.000 0.001 0.001 rpc.py:574(__getmethods)
20 0.000 0.000 0.000 0.000 rpc.py:598(__init__)
20 0.000 0.000 0.050 0.002 rpc.py:603(__call__)
20 0.000 0.000 0.051 0.003 run.py:340(write)
1 20.722 20.722 20.773 20.773 test.py:3(foo)
42 0.000 0.000 0.000 0.000 threading.py:1226(current_thread)
21 0.000 0.000 0.000 0.000 threading.py:215(__init__)
21 0.000 0.000 0.047 0.002 threading.py:263(wait)
21 0.000 0.000 0.000 0.000 threading.py:74(RLock)
21 0.000 0.000 0.000 0.000 {built-in method _struct.pack}
21 0.000 0.000 0.000 0.000 {built-in method _thread.allocate_lock}
42 0.000 0.000 0.000 0.000 {built-in method _thread.get_ident}
1 0.000 0.000 20.773 20.773 {built-in method builtins.exec}
42 0.000 0.000 0.000 0.000 {built-in method builtins.isinstance}
63 0.000 0.000 0.000 0.000 {built-in method builtins.len}
10 0.000 0.000 0.051 0.005 {built-in method builtins.print}
21 0.000 0.000 0.000 0.000 {built-in method select.select}
21 0.000 0.000 0.000 0.000 {method '_acquire_restore' of '_thread.RLock' objects}
21 0.000 0.000 0.000 0.000 {method '_is_owned' of '_thread.RLock' objects}
21 0.000 0.000 0.000 0.000 {method '_release_save' of '_thread.RLock' objects}
21 0.000 0.000 0.000 0.000 {method 'acquire' of '_thread.RLock' objects}
42 0.047 0.001 0.047 0.001 {method 'acquire' of '_thread.lock' objects}
21 0.000 0.000 0.000 0.000 {method 'append' of 'collections.deque' objects}
1 0.000 0.000 0.000 0.000 {method 'disable' of '_lsprof.Profiler' objects}
21 0.000 0.000 0.000 0.000 {method 'dump' of '_pickle.Pickler' objects}
20 0.000 0.000 0.000 0.000 {method 'get' of 'dict' objects}
21 0.000 0.000 0.000 0.000 {method 'getvalue' of '_io.BytesIO' objects}
21 0.000 0.000 0.000 0.000 {method 'release' of '_thread.RLock' objects}
21 0.001 0.000 0.001 0.000 {method 'send' of '_socket.socket' objects}
我期待它能告诉我我的代码的哪些部分花费了最长的时间,例如它显示 a = i**i
花费了最长的计算时间,但是我只能从它的内容中收集到告诉我的是 foo()
函数花费的时间最长,但是我从数据中不知道该函数内部花费的时间最长。
此外,当我将它实现到我的实际代码中时,它会做同样的事情。一切都在函数中,它只告诉我哪些函数花费的时间最长,而不是函数中的哪些花费了这么长时间。
所以这是我的主要问题:
如何查看函数内部的内容使代码花费这么长时间(我是否应该使用 cProfile
?)
一旦我知道使用最多的是什么,着手优化我的代码的最佳方法是什么CPU
注意: 我的 RAM 和磁盘等绝对没问题,只是 CPU 已经用完了(12% CPU 因为它只运行s 在单核上)
How do I see what inside the function is making the code take so long (Should I even use cProfile?)
是的,您可以使用 cProfile
,但您问问题的方式让我怀疑 line_profiler
(第三方模块,您需要安装它)是否是一个更好的工具。
当我想分析一个函数时,我正在使用这个包的 IPython/Jupyter 绑定:
%load_ext line_profiler
实际分析函数:
%lprun -f foo foo()
# ^^^^^---- this call will be profiled
# ^^^-----------function to profile
产生此输出:
Timer unit: 5.58547e-07 s
Total time: 17.1189 s
File: <ipython-input-1-21b5a5f52f66>
Function: foo at line 1
Line # Hits Time Per Hit % Time Line Contents
==============================================================
1 def foo():
2 10001 31906 3.2 0.1 for i in range(10000):
3 10000 30534065 3053.4 99.6 a = i**i
4 10000 75998 7.6 0.2 if i % 1000 == 0:
5 10 6953 695.3 0.0 print(i)
其中包括一些可能有趣的内容。例如 99.6%
的时间花费在 i**i
行。
- What is the best way to set about optimising my code once I know what is using the most CPU
这取决于。有时您需要使用不同的 functions/datastructures/algorithms - 有时您无能为力。但至少你知道你的瓶颈在哪里,你可以估计瓶颈或其他地方的变化会有多大影响。
正如您在分析日志中注意到的那样,cProfile
最大分辨率是 函数。
所以:
- 如果您的函数很小,您也许可以找出哪个部分花费了很长时间(尽管使用
in
等内置调用有时会很困难)
- 如果您的函数很大,也许是时候将其缩减为更小的函数,即 "profileable",但是函数调用开销可能会减慢速度
在上一个问题中,我问的是多处理,使用多核使程序 运行 更快,有人告诉我:
More often than not, you can get a 100x+ optimization with better code compared to a 4x improvement and additional complexities with multiprocessing
然后他们建议我应该:
Use a profiler to understand what is slow, then focus on optimizing that.
所以我去了这个问题:How can you profile a script?
在这里我找到了 cProfile
并将其实现到一些测试代码中以查看其工作原理。
这是我的代码:
import cProfile
def foo():
for i in range(10000):
a = i**i
if i % 1000 == 0:
print(i)
cProfile.run('foo()')
然而,在 运行 之后,这就是我得到的:
0
1000
2000
3000
4000
5000
6000
7000
8000
9000
1018 function calls in 20.773 seconds
Ordered by: standard name
ncalls tottime percall cumtime percall filename:lineno(function)
1 0.000 0.000 20.773 20.773 <string>:1(<module>)
147 0.000 0.000 0.000 0.000 rpc.py:150(debug)
21 0.000 0.000 0.050 0.002 rpc.py:213(remotecall)
21 0.000 0.000 0.002 0.000 rpc.py:223(asynccall)
21 0.000 0.000 0.048 0.002 rpc.py:243(asyncreturn)
21 0.000 0.000 0.000 0.000 rpc.py:249(decoderesponse)
21 0.000 0.000 0.048 0.002 rpc.py:287(getresponse)
21 0.000 0.000 0.000 0.000 rpc.py:295(_proxify)
21 0.001 0.000 0.048 0.002 rpc.py:303(_getresponse)
21 0.000 0.000 0.000 0.000 rpc.py:325(newseq)
21 0.000 0.000 0.002 0.000 rpc.py:329(putmessage)
21 0.000 0.000 0.000 0.000 rpc.py:55(dumps)
20 0.000 0.000 0.001 0.000 rpc.py:556(__getattr__)
1 0.000 0.000 0.001 0.001 rpc.py:574(__getmethods)
20 0.000 0.000 0.000 0.000 rpc.py:598(__init__)
20 0.000 0.000 0.050 0.002 rpc.py:603(__call__)
20 0.000 0.000 0.051 0.003 run.py:340(write)
1 20.722 20.722 20.773 20.773 test.py:3(foo)
42 0.000 0.000 0.000 0.000 threading.py:1226(current_thread)
21 0.000 0.000 0.000 0.000 threading.py:215(__init__)
21 0.000 0.000 0.047 0.002 threading.py:263(wait)
21 0.000 0.000 0.000 0.000 threading.py:74(RLock)
21 0.000 0.000 0.000 0.000 {built-in method _struct.pack}
21 0.000 0.000 0.000 0.000 {built-in method _thread.allocate_lock}
42 0.000 0.000 0.000 0.000 {built-in method _thread.get_ident}
1 0.000 0.000 20.773 20.773 {built-in method builtins.exec}
42 0.000 0.000 0.000 0.000 {built-in method builtins.isinstance}
63 0.000 0.000 0.000 0.000 {built-in method builtins.len}
10 0.000 0.000 0.051 0.005 {built-in method builtins.print}
21 0.000 0.000 0.000 0.000 {built-in method select.select}
21 0.000 0.000 0.000 0.000 {method '_acquire_restore' of '_thread.RLock' objects}
21 0.000 0.000 0.000 0.000 {method '_is_owned' of '_thread.RLock' objects}
21 0.000 0.000 0.000 0.000 {method '_release_save' of '_thread.RLock' objects}
21 0.000 0.000 0.000 0.000 {method 'acquire' of '_thread.RLock' objects}
42 0.047 0.001 0.047 0.001 {method 'acquire' of '_thread.lock' objects}
21 0.000 0.000 0.000 0.000 {method 'append' of 'collections.deque' objects}
1 0.000 0.000 0.000 0.000 {method 'disable' of '_lsprof.Profiler' objects}
21 0.000 0.000 0.000 0.000 {method 'dump' of '_pickle.Pickler' objects}
20 0.000 0.000 0.000 0.000 {method 'get' of 'dict' objects}
21 0.000 0.000 0.000 0.000 {method 'getvalue' of '_io.BytesIO' objects}
21 0.000 0.000 0.000 0.000 {method 'release' of '_thread.RLock' objects}
21 0.001 0.000 0.001 0.000 {method 'send' of '_socket.socket' objects}
我期待它能告诉我我的代码的哪些部分花费了最长的时间,例如它显示 a = i**i
花费了最长的计算时间,但是我只能从它的内容中收集到告诉我的是 foo()
函数花费的时间最长,但是我从数据中不知道该函数内部花费的时间最长。
此外,当我将它实现到我的实际代码中时,它会做同样的事情。一切都在函数中,它只告诉我哪些函数花费的时间最长,而不是函数中的哪些花费了这么长时间。
所以这是我的主要问题:
如何查看函数内部的内容使代码花费这么长时间(我是否应该使用
cProfile
?)一旦我知道使用最多的是什么,着手优化我的代码的最佳方法是什么CPU
注意: 我的 RAM 和磁盘等绝对没问题,只是 CPU 已经用完了(12% CPU 因为它只运行s 在单核上)
How do I see what inside the function is making the code take so long (Should I even use cProfile?)
是的,您可以使用 cProfile
,但您问问题的方式让我怀疑 line_profiler
(第三方模块,您需要安装它)是否是一个更好的工具。
当我想分析一个函数时,我正在使用这个包的 IPython/Jupyter 绑定:
%load_ext line_profiler
实际分析函数:
%lprun -f foo foo()
# ^^^^^---- this call will be profiled
# ^^^-----------function to profile
产生此输出:
Timer unit: 5.58547e-07 s
Total time: 17.1189 s
File: <ipython-input-1-21b5a5f52f66>
Function: foo at line 1
Line # Hits Time Per Hit % Time Line Contents
==============================================================
1 def foo():
2 10001 31906 3.2 0.1 for i in range(10000):
3 10000 30534065 3053.4 99.6 a = i**i
4 10000 75998 7.6 0.2 if i % 1000 == 0:
5 10 6953 695.3 0.0 print(i)
其中包括一些可能有趣的内容。例如 99.6%
的时间花费在 i**i
行。
- What is the best way to set about optimising my code once I know what is using the most CPU
这取决于。有时您需要使用不同的 functions/datastructures/algorithms - 有时您无能为力。但至少你知道你的瓶颈在哪里,你可以估计瓶颈或其他地方的变化会有多大影响。
正如您在分析日志中注意到的那样,cProfile
最大分辨率是 函数。
所以:
- 如果您的函数很小,您也许可以找出哪个部分花费了很长时间(尽管使用
in
等内置调用有时会很困难) - 如果您的函数很大,也许是时候将其缩减为更小的函数,即 "profileable",但是函数调用开销可能会减慢速度