5

我目前正在学习如何使用cProfile,我有一些疑问。

我目前正在尝试分析以下脚本:

import time

def fast():
    print("Fast!")

def slow():
    time.sleep(3)
    print("Slow!")

def medium():
    time.sleep(0.5)
    print("Medium!")

fast()
slow()
medium()

我执行命令python -m cProfile test_cprofile.py,我得到以下结果:

Fast!
Slow!
Medium!
     7 function calls in 3.504 seconds

Ordered by: standard name

ncalls  tottime  percall  cumtime  percall filename:lineno(function)
    1    0.000    0.000    3.504    3.504 test_cprofile.py:1(<module>)
    1    0.000    0.000    0.501    0.501 test_cprofile.py:10(medium)
    1    0.000    0.000    0.000    0.000 test_cprofile.py:3(fast)
    1    0.000    0.000    3.003    3.003 test_cprofile.py:6(slow)
    1    0.000    0.000    0.000    0.000 {method 'disable' of '_lsprof.Profiler' objects}
    2    3.504    1.752    3.504    1.752 {time.sleep}

但是,当我在顶部使用 pylab 导入(例如(import pylab))编辑脚本时,输出cProfile非常大。我试图限制使用的行数,python -m cProfile test_cprofile.py | head -n 10但是我收到以下错误:

Traceback (most recent call last):
File "/home/user/anaconda/lib/python2.7/runpy.py", line 162, in _run_module_as_main
"__main__", fname, loader, pkg_name)
File "/home/user/anaconda/lib/python2.7/runpy.py", line 72, in _run_code
exec code in run_globals
File "/home/user/anaconda/lib/python2.7/cProfile.py", line 199, in <module>
main()
File "/home/user/anaconda/lib/python2.7/cProfile.py", line 192, in main
runctx(code, globs, None, options.outfile, options.sort)
File "/home/user/anaconda/lib/python2.7/cProfile.py", line 56, in runctx
result = prof.print_stats(sort)
File "/home/user/anaconda/lib/python2.7/cProfile.py", line 81, in print_stats
pstats.Stats(self).strip_dirs().sort_stats(sort).print_stats()
File "/home/user/anaconda/lib/python2.7/pstats.py", line 360, in print_stats
self.print_line(func)
File "/home/user/anaconda/lib/python2.7/pstats.py", line 438, in print_line
print >> self.stream, c.rjust(9),
IOError: [Errno 32] Broken pipe

有人可以帮助解决与此类似的情况的正确程序,在这种情况下,我们有一个import pylab或另一个模块可以生成如此高的输出信息cProfile吗?

4

2 回答 2

5

我不知道有什么方法可以像您一样通过cProfile直接从命令行运行模块来进行您想要的选择性分析。

但是,您可以通过将代码修改为显式import模块来做到这一点,但您必须自己做所有事情。以下是如何对您的示例代码执行此操作:

(注意:以下代码同时兼容 Python 2 和 3。)

from cProfile import Profile
from pstats import Stats
prof = Profile()

prof.disable()  # i.e. don't time imports
import time
prof.enable()  # profiling back on

def fast():
    print("Fast!")

def slow():
    time.sleep(3)
    print("Slow!")

def medium():
    time.sleep(0.5)
    print("Medium!")

fast()
slow()
medium()

prof.disable()  # don't profile the generation of stats
prof.dump_stats('mystats.stats')

with open('mystats_output.txt', 'wt') as output:
    stats = Stats('mystats.stats', stream=output)
    stats.sort_stats('cumulative', 'time')
    stats.print_stats()

mystats_output.txt之后的文件内容:

Sun Aug 02 16:55:38 2015    mystats.stats

         6 function calls in 3.522 seconds

   Ordered by: cumulative time, internal time

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
        2    3.522    1.761    3.522    1.761 {time.sleep}
        1    0.000    0.000    3.007    3.007 cprofile-with-imports.py:15(slow)
        1    0.000    0.000    0.515    0.515 cprofile-with-imports.py:19(medium)
        1    0.000    0.000    0.000    0.000 cprofile-with-imports.py:12(fast)
        1    0.000    0.000    0.000    0.000 {method 'disable' of '_lsprof.Profiler' objects}

更新:

您可以通过使用上下文管理器Profile方法派生您自己的类来自动化事情,从而使分析变得更容易一些。我没有添加一个名称类似的方法,而是实现了它,以便您可以在语句中调用类实例。每当退出语句控制的上下文时,分析将自动关闭。enable_profiling()withwith

这是课程:

from contextlib import contextmanager
from cProfile import Profile
from pstats import Stats

class Profiler(Profile):
    """ Custom Profile class with a __call__() context manager method to
        enable profiling.
    """
    def __init__(self, *args, **kwargs):
        super(Profile, self).__init__(*args, **kwargs)
        self.disable()  # Profiling initially off.

    @contextmanager
    def __call__(self):
        self.enable()
        yield  # Execute code to be profiled.
        self.disable()

使用它而不是 stockProfile对象看起来像这样:

profiler = Profiler()  # Create class instance.

import time  # Import won't be profiled since profiling is initially off.

with profiler():  # Call instance to enable profiling.
    def fast():
        print("Fast!")

    def slow():
        time.sleep(3)
        print("Slow!")

    def medium():
        time.sleep(0.5)
        print("Medium!")

    fast()
    slow()
    medium()

profiler.dump_stats('mystats.stats')  # Stats output generation won't be profiled.

with open('mystats_output.txt', 'wt') as output:
    stats = Stats('mystats.stats', stream=output)
    stats.strip_dirs().sort_stats('cumulative', 'time')
    stats.print_stats()

# etc...

由于它是一个Profile子类,所有基类的方法,如dump_stats()所有仍然可用,如图所示。

当然,您可以更进一步,例如添加一种方法来生成统计数据并以某种自定义方式对其进行格式化。

于 2015-08-03T00:04:24.870 回答
2

如果您稍微更改脚本,那么在不分析导入的情况下分析脚本会容易得多。

test_cprofiler.py

import time
import pylab

def fast():
    print("Fast!")

def slow():
    time.sleep(3)
    print("Slow!")

def medium():
    time.sleep(0.5)
    print("Medium!")

def main():
    fast()
    slow()
    medium()

if __name__ == "__main__":
    main()

分析器.py

import cProfile

import test_cprofiler

cProfile.run("test_cprofiler.main()")

运行为:

python profiler.py

产生以下输出:

Fast!
Slow!
Medium!
         8 function calls in 3.498 seconds

   Ordered by: standard name

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
        1    0.000    0.000    3.498    3.498 <string>:1(<module>)
        1    0.000    0.000    2.998    2.998 run.py:11(slow)
        1    0.000    0.000    3.498    3.498 run.py:15(main)
        1    0.000    0.000    0.000    0.000 run.py:4(fast)
        1    0.000    0.000    0.500    0.500 run.py:7(medium)
        1    0.000    0.000    0.000    0.000 {method 'disable' of '_lsprof.Profiler' objects}
        2    3.498    1.749    3.498    1.749 {time.sleep}
于 2015-08-03T06:17:00.937 回答