暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

python 内存系列(3)-使用cProfile模块对程序进行可视化性能分析

小儿来一壶枸杞酒泡茶 2021-03-05
1668

cProfile模块的应用分析(化性能分析)

除了使用line_profiler,我们还可以使用cProfile是标准库内建的模块来分析。

cProfile模块也是比较常用的分析器。结合Gprof2Dot 将分析器输出转换成 Graphviz 可处理的图像表述,我们就可以从图里分享相关的即可得到不同函数所消耗的时间分布信息。

  • c_profile_text_run.py
import random


def randomlist(n):
    lists = []
    l = [random.random() for i in range(n)]
    l.sort()
    for v in l:
        lists.append(v)
    return lists


if __name__ == "__main__":
    randomlist(20)



执行分析代码:

python -m cProfile -s cumulative .\c_profile_text_run.py

分析结果:

(.venv) PS D:\code\vscode\py> python -m cProfile -s cumulative .\c_profile_text_run.py
         1095 function calls (1073 primitive calls) in 0.011 seconds

   Ordered by: cumulative time

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
      3/1    0.000    0.000    0.011    0.011 {built-in method builtins.exec}
        1    0.000    0.000    0.011    0.011 c_profile_text_run.py:9(<module>)
      5/1    0.000    0.000    0.011    0.011 <frozen importlib._bootstrap>:965(_find_and_load)
      5/1    0.000    0.000    0.011    0.011 <frozen importlib._bootstrap>:938(_find_and_load_unlocked)  
      5/1    0.000    0.000    0.009    0.009 <frozen importlib._bootstrap>:659(_load_unlocked)
      2/1    0.000    0.000    0.009    0.009 <frozen importlib._bootstrap_external>:691(exec_module)     
      8/1    0.000    0.000    0.009    0.009 <frozen importlib._bootstrap>:214(_call_with_frames_removed)
        1    0.000    0.000    0.009    0.009 random.py:37(<module>)
        1    0.000    0.000    0.007    0.007 hashlib.py:53(<module>)
        5    0.000    0.000    0.006    0.001 <frozen importlib._bootstrap>:570(module_from_spec)
        1    0.000    0.000    0.006    0.006 <frozen importlib._bootstrap_external>:935(create_module)   
        1    0.006    0.006    0.006    0.006 {built-in method _imp.create_dynamic}
        5    0.000    0.000    0.004    0.001 <frozen importlib._bootstrap>:879(_find_spec)
        3    0.000    0.000    0.003    0.001 <frozen importlib._bootstrap_external>:1165(find_spec)      
        3    0.000    0.000    0.003    0.001 <frozen importlib._bootstrap_external>:1133(_get_spec)      
       17    0.000    0.000    0.003    0.000 <frozen importlib._bootstrap_external>:75(_path_stat)       
       11    0.000    0.000    0.003    0.000 <frozen importlib._bootstrap_external>:1247(find_spec)      
       17    0.003    0.000    0.003    0.000 {built-in method nt.stat}
        2    0.000    0.000    0.001    0.001 <frozen importlib._bootstrap_external>:761(get_code)
        4    0.000    0.000    0.001    0.000 <frozen importlib._bootstrap_external>:85(_path_is_mode_type)
        3    0.000    0.000    0.001    0.000 <frozen importlib._bootstrap_external>:94(_path_isfile)
        3    0.000    0.000    0.000    0.000 <frozen importlib._bootstrap_external>:645(find_spec)
        3    0.000    0.000    0.000    0.000 <frozen importlib._bootstrap_external>:630(_search_registry)
        2    0.000    0.000    0.000    0.000 <frozen importlib._bootstrap_external>:848(get_data)
        3    0.000    0.000    0.000    0.000 <frozen importlib._bootstrap_external>:623(_open_registry)
        6    0.000    0.000    0.000    0.000 {built-in method winreg.OpenKey}
        2    0.000    0.000    0.000    0.000 <frozen importlib._bootstrap_external>:858(path_stats)
       14    0.000    0.000    0.000    0.000 <frozen importlib._bootstrap_external>:1096(_path_importer_cache)
        1    0.000    0.000    0.000    0.000 <frozen importlib._bootstrap_external>:1079(_path_hooks)
        1    0.000    0.000    0.000    0.000 <frozen importlib._bootstrap_external>:1292(_fill_cache)
        1    0.000    0.000    0.000    0.000 <frozen importlib._bootstrap_external>:1333(path_hook_for_FileFinder)
        2    0.000    0.000    0.000    0.000 <frozen importlib._bootstrap_external>:506(_compile_bytecode)
        1    0.000    0.000    0.000    0.000 {built-in method nt.listdir}
        1    0.000    0.000    0.000    0.000 <frozen importlib._bootstrap_external>:99(_path_isdir)
        2    0.000    0.000    0.000    0.000 {built-in method marshal.loads}
       52    0.000    0.000    0.000    0.000 <frozen importlib._bootstrap_external>:57(_path_join)
        5    0.000    0.000    0.000    0.000 <frozen importlib._bootstrap>:510(_init_module_attrs)
        2    0.000    0.000    0.000    0.000 {method 'read' of '_io.FileIO' objects}
        5    0.000    0.000    0.000    0.000 <frozen importlib._bootstrap>:163(__enter__)
       52    0.000    0.000    0.000    0.000 <frozen importlib._bootstrap_external>:59(<listcomp>)
        2    0.000    0.000    0.000    0.000 <frozen importlib._bootstrap>:737(create_module)
        2    0.000    0.000    0.000    0.000 {built-in method _imp.create_builtin}
        5    0.000    0.000    0.000    0.000 <frozen importlib._bootstrap>:176(_get_module_lock)
        4    0.000    0.000    0.000    0.000 <frozen importlib._bootstrap_external>:276(cache_from_source)
        5    0.000    0.000    0.000    0.000 <frozen importlib._bootstrap>:406(cached)
        2    0.000    0.000    0.000    0.000 {built-in method builtins.__build_class__}
        3    0.000    0.000    0.000    0.000 <frozen importlib._bootstrap_external>:372(_get_cached)
        1    0.000    0.000    0.000    0.000 random.py:84(__init__)
        2    0.000    0.000    0.000    0.000 <frozen importlib._bootstrap_external>:449(_validate_bytecode_header)
        1    0.000    0.000    0.000    0.000 random.py:93(seed)
       65    0.000    0.000    0.000    0.000 {method 'format' of 'str' objects}
        5    0.000    0.000    0.000    0.000 <frozen importlib._bootstrap>:716(find_spec)
        5    0.000    0.000    0.000    0.000 <frozen importlib._bootstrap>:74(__init__)
        3    0.000    0.000    0.000    0.000 <frozen importlib._bootstrap_external>:1242(_get_spec)
        1    0.000    0.000    0.000    0.000 c_profile_text_run.py:14(randomlist)
        5    0.000    0.000    0.000    0.000 <frozen importlib._bootstrap>:321(__exit__)
        4    0.000    0.000    0.000    0.000 <frozen importlib._bootstrap_external>:63(_path_split)
       10    0.000    0.000    0.000    0.000 {built-in method _thread.allocate_lock}
       54    0.000    0.000    0.000    0.000 <frozen importlib._bootstrap_external>:396(_verbose_message)
        6    0.000    0.000    0.000    0.000 hashlib.py:98(__get_openssl_constructor)
       33    0.000    0.000    0.000    0.000 {built-in method builtins.hasattr}
        5    0.000    0.000    0.000    0.000 <frozen importlib._bootstrap>:170(__exit__)
       32    0.000    0.000    0.000    0.000 {built-in method builtins.getattr}
        2    0.000    0.000    0.000    0.000 <frozen importlib._bootstrap>:436(spec_from_loader)
      108    0.000    0.000    0.000    0.000 {method 'rstrip' of 'str' objects}
        1    0.000    0.000    0.000    0.000 <frozen importlib._bootstrap_external>:1210(__init__)
        5    0.000    0.000    0.000    0.000 <frozen importlib._bootstrap>:94(acquire)
        3    0.000    0.000    0.000    0.000 <frozen importlib._bootstrap_external>:545(spec_from_file_location)
       56    0.000    0.000    0.000    0.000 {method 'join' of 'str' objects}
        5    0.000    0.000    0.000    0.000 <frozen importlib._bootstrap>:119(release)
        8    0.000    0.000    0.000    0.000 <frozen importlib._bootstrap>:995(_handle_fromlist)
        1    0.000    0.000    0.000    0.000 {built-in method nt.urandom}
        1    0.000    0.000    0.000    0.000 {function Random.seed at 0x024A27C8}
        1    0.000    0.000    0.000    0.000 c_profile_text_run.py:16(<listcomp>)
        1    0.000    0.000    0.000    0.000 {built-in method math.exp}
        5    0.000    0.000    0.000    0.000 {built-in method _imp.is_builtin}
        5    0.000    0.000    0.000    0.000 {built-in method builtins.any}
       14    0.000    0.000    0.000    0.000 <frozen importlib._bootstrap>:856(__exit__)
        1    0.000    0.000    0.000    0.000 random.py:68(Random)
       14    0.000    0.000    0.000    0.000 <frozen importlib._bootstrap>:852(__enter__)
        1    0.000    0.000    0.000    0.000 <frozen importlib._bootstrap_external>:1321(<setcomp>)
       25    0.000    0.000    0.000    0.000 {method 'rpartition' of 'str' objects}
        5    0.000    0.000    0.000    0.000 <frozen importlib._bootstrap>:419(parent)
        4    0.000    0.000    0.000    0.000 <frozen importlib._bootstrap_external>:52(_r_long)
        3    0.000    0.000    0.000    0.000 {method 'extend' of 'list' objects}
        5    0.000    0.000    0.000    0.000 {built-in method from_bytes}
        4    0.000    0.000    0.000    0.000 {method 'rsplit' of 'str' objects}
        1    0.000    0.000    0.000    0.000 <frozen importlib._bootstrap_external>:943(exec_module)
       11    0.000    0.000    0.000    0.000 <frozen importlib._bootstrap_external>:37(_relax_case)
        2    0.000    0.000    0.000    0.000 <frozen importlib._bootstrap>:235(_requires_builtin_wrapper)
        5    0.000    0.000    0.000    0.000 <frozen importlib._bootstrap>:372(__init__)
        2    0.000    0.000    0.000    0.000 <frozen importlib._bootstrap>:745(exec_module)
       16    0.000    0.000    0.000    0.000 {method 'partition' of 'str' objects}
       20    0.000    0.000    0.000    0.000 <frozen importlib._bootstrap>:324(<genexpr>)
       16    0.000    0.000    0.000    0.000 {method 'add' of 'set' objects}
       30    0.000    0.000    0.000    0.000 {method 'lower' of 'str' objects}
       17    0.000    0.000    0.000    0.000 {built-in method builtins.isinstance}
        5    0.000    0.000    0.000    0.000 <frozen importlib._bootstrap>:314(__enter__)
        3    0.000    0.000    0.000    0.000 <frozen importlib._bootstrap>:789(find_spec)
       19    0.000    0.000    0.000    0.000 {built-in method _imp.release_lock}
        5    0.000    0.000    0.000    0.000 <frozen importlib._bootstrap>:310(__init__)
        5    0.000    0.000    0.000    0.000 <frozen importlib._bootstrap>:159(__init__)
        1    0.000    0.000    0.000    0.000 {built-in method _hashlib.openssl_md5}
       20    0.000    0.000    0.000    0.000 {method 'random' of '_random.Random' objects}
        2    0.000    0.000    0.000    0.000 <frozen importlib._bootstrap>:35(_new_module)
        2    0.000    0.000    0.000    0.000 <frozen importlib._bootstrap_external>:818(__init__)
        4    0.000    0.000    0.000    0.000 {method 'endswith' of 'str' objects}
        1    0.000    0.000    0.000    0.000 {method 'union' of 'set' objects}
        2    0.000    0.000    0.000    0.000 <frozen importlib._bootstrap_external>:412(_check_name_wrapper)
        3    0.000    0.000    0.000    0.000 {built-in method nt.getcwd}
        1    0.000    0.000    0.000    0.000 {method 'sort' of 'list' objects}
        5    0.000    0.000    0.000    0.000 <frozen importlib._bootstrap>:225(_verbose_message)
       14    0.000    0.000    0.000    0.000 {built-in method _imp.acquire_lock}
        1    0.000    0.000    0.000    0.000 {built-in method math.sqrt}
        5    0.000    0.000    0.000    0.000 <frozen importlib._bootstrap>:190(cb)
       10    0.000    0.000    0.000    0.000 {built-in method _thread.get_ident}
       20    0.000    0.000    0.000    0.000 {method 'append' of 'list' objects}
        2    0.000    0.000    0.000    0.000 {method 'startswith' of 'str' objects}
        8    0.000    0.000    0.000    0.000 <frozen importlib._bootstrap_external>:1216(<genexpr>)
        5    0.000    0.000    0.000    0.000 <frozen importlib._bootstrap>:427(has_location)
        3    0.000    0.000    0.000    0.000 {built-in method _imp.is_frozen}
        8    0.000    0.000    0.000    0.000 {built-in method builtins.len}
        1    0.000    0.000    0.000    0.000 random.py:648(SystemRandom)
        1    0.000    0.000    0.000    0.000 <frozen importlib._bootstrap_external>:924(__init__)
        2    0.000    0.000    0.000    0.000 {built-in method _imp._fix_co_filename}
        2    0.000    0.000    0.000    0.000 {built-in method math.log}
        2    0.000    0.000    0.000    0.000 {built-in method _imp.exec_builtin}
        6    0.000    0.000    0.000    0.000 {built-in method builtins.globals}
        1    0.000    0.000    0.000    0.000 {built-in method _hashlib.openssl_sha384}
        1    0.000    0.000    0.000    0.000 {built-in method _hashlib.openssl_sha1}
        2    0.000    0.000    0.000    0.000 <frozen importlib._bootstrap>:762(is_package)
        2    0.000    0.000    0.000    0.000 <frozen importlib._bootstrap_external>:843(get_filename)
        1    0.000    0.000    0.000    0.000 {built-in method _imp.exec_dynamic}
        1    0.000    0.000    0.000    0.000 {built-in method _hashlib.openssl_sha224}
        2    0.000    0.000    0.000    0.000 <frozen importlib._bootstrap_external>:688(create_module)
        1    0.000    0.000    0.000    0.000 {built-in method _hashlib.openssl_sha256}
        1    0.000    0.000    0.000    0.000 {built-in method _hashlib.openssl_sha512}
        1    0.000    0.000    0.000    0.000 {method 'disable' of '_lsprof.Profiler' objects}


相关参数的说明:

  • ncalls:表示函数调用的次数;
  • tottime:表示指定函数的总的运行时间,除掉函数中调用子函数的运行时间;
  • percall:(第一个percall)等于 tottime/ncalls;
  • cumtime:表示该函数及其所有子函数的调用运行的时间,即函数开始调用到返回的时间;
  • percall:(第二个percall)即函数运行一次的平均时间,等于 cumtime/ncalls;
  • filename:lineno(function):每个函数调用的具体信息;

需要注意的是cProfile很难搞清楚函数内的每一行发生了什么,是针对整个函数来说的。上面的输出的信息比较难以分析,我们的可以把结果输出到文件中,然后结合pstats进行分析。

输出文件方式进行分析:

python -m cProfile -o runtest_profile.stats .\c_profile_text_run.py

然后使用我们的pstats进行文件分析:

  • runtest_profile_stats_run.py

import pstats
p = pstats.Stats("runtest_profile.stats")
p.sort_stats("cumulative")  # 和显示明细一样
p.print_stats()
p.print_callers()  # 可以显示函数被哪些函数调用
p.print_callees()  # 可以显示哪个函数调用了哪些函数


然后进行分析结果的查看。

可惜上面结果显示还是比较凌乱不堪啊!你也不愿意去看的吧!!哈哈

我们需要一个可视化的方式来查看分析结果:

这个时候我们可以结合Gprof2Dot可视化工具来进行分析结果的可视化。

执行分析命令过程中出现的错误:

(.venv) PS D:\code\vscode\py> python gprof2dot.py -f pstats .\runtest_profile.stats | dot -Tpng -o runtest_result.png

错误提示:

原因是:python需要调用安装后的dot.exe,因此需要配置一下graphviz本地的软件的系统环境。

下载地址:

https://graphviz.org/_pages/Download/Download_windows.html

安装的过程其实可以直接的添加到系统环境中:

配置环境变量:

安装graphviz依赖库:

pip install graphviz

安装好后再执行:

(.venv) PS D:\code\vscode\py> python gprof2dot.py -f pstats .\runtest_profile.stats | dot -Tpng -o runtest_result.png

结果还是:

dot : 无法将“dot”项识别为 cmdlet、函数、脚本文件或可运行程序的名称。请检查名称的拼写,如果包括路径,请确保路径正确,然后再试一次。
所在位置 行:1 字符: 57
+ python gprof2dot.py -f pstats .\runtest_profile.stats | dot -Tpng -o  ...
+                                                         ~~~
    + CategoryInfo          : ObjectNotFound: (dot:String) [], CommandNotFoundException
    + FullyQualifiedErrorId : CommandNotFoundException

重启电话后再执行:

直接結果:

(.venv) PS D:\code\vscode\py> python gprof2dot.py -f pstats result.out | dot  -Tpng -o result.png                                  
D:\code\vscode\py\.venv\Scripts\python.exe: can't open file 'gprof2dot.py': [Errno 2] No such file or directory
(.venv) PS D:\code\vscode\py> 

测试库的使用:

from graphviz import Digraph

g = Digraph('测试图片')
g.node(name='a', color='red')
g.node(name='b', color='blue')
g.edge('a''b', color='green')
g.view()


分析意思是找不到这个文件??还是需要再安装一个库https://github.com/jrfonseca/gprof2dot

pip install gprof2dot

貌似还是不行?手动的还是把gprof2dot.py代码拷贝一份吧!放在本地运行。

然后,再执行命令!

(.venv) PS D:\code\vscode\py> python gprof2dot.py -f pstats result.out | dot  -Tpng -o result.png
(.venv) PS D:\code\vscode\py>


最终的可视化图来了!

可视化结果步骤总结:

  • 下载安装graphviz
  • 添加安装路径到环境变量
  • pip安装graphviz的python还有gprof2dot
    • pip install graphviz
    • pip install gprof2dot(看运气吧,不行就搞源码下来)

使用代码装饰器的形式进行代码分析

代码来源:https://testerhome.com/topics/21411

首先封装我们装饰器库

'''
Author: 小钟同学
objectDescription: 项目描述
Date: 2021-03-05 15:42:33
LastEditors: 308711822@qq.com
LastEditTime: 2021-03-05 17:37:15
FilePath: \py\c_profile_text_run.py
Version: 1.0
'
''


import random
import cProfile
import pstats
import os
# 性能分析装饰器定义


def do_cprofile(filename, DO_PROF=True):
    def wrapper(func):
        def profiled_func(*args, **kwargs):
            if DO_PROF:
                profile = cProfile.Profile()
                profile.enable()
                result = func(*args, **kwargs)
                profile.disable()
                # Sort stat by internal time.
                sortby = "tottime"
                ps = pstats.Stats(profile).sort_stats(sortby)
                ps.dump_stats(filename)
            else:
                result = func(*args, **kwargs)
            return result
        return profiled_func
    return wrapper


# ==============================


# 在需要进行分析的地方进行性能分析,直接使用装饰器进行装饰即可
@do_cprofile("./result.out")
def randomlist(n):
    lists = []
    l = [random.random() for i in range(n)]
    l.sort()
    for v in l:
        lists.append(v)
    return lists


if __name__ == "__main__":
    randomlist(20)



1、然后右键运行我们的程序,然后生产我们的分析文件。2、运行分析命令:

python gprof2dot.py -f pstats result.out | dot  -Tpng -o result.png

3、查看分析图:

说明

部分的图或资料来互联网收集整理,如有侵权,烦请联系,我会立即进行删除。

End

纯属个人实践中相关经验之谈,如有纰漏,还希望大佬们多多提点!小钟同学 | 文  【原创】| QQ:308711822


文章转载自小儿来一壶枸杞酒泡茶,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论