Scalene: Python 高性能 CPU 内存分析器

技术

picture.image

关于 Scalene

Scalene 是一个 Python 的高性能 CPU内存分析器,它可以做到很多其他Python分析器不能做到的事情。它在能提供更多详细信息的同时,比其他的分析器要快几个数量级。

  1. Scalene 是 很快的。它使用采样的方式而不是直接测量或者依靠Python的追踪工具。它的开销一般不超过10-20% (通常更少)。
  2. Scalene 是 精确的。和大部分其他的Python分析器不同,Scalene 在 行级别 下执行CPU分析,在你的程序中指出对应代码行的执行时间。和大多数分析器所返回的功能级分析结果相比,这种程度的细节可能会更有用。
  3. Scalane 可以区分在Python中运行的时间和在native代码(包括库)中花费的时间。大多数的Python程序员并不会去优化native代码(通常在Python实现中或者所依赖的外部库),所以区分这两种运行时间,有助于开发者能够将优化的工作专注于他们能够实际改善的代码上。
  4. Scalene 可以 分析内存使用情况。除了追踪CPU使用情况,Scalene还指出对应代码行的内存增长。这是通过指定内存分配器来实现的。
  5. NEW! Scalene 会生成 每行 的内存分析,以此更容易的追踪内存泄露。
  6. NEW! Scalene 会分析 内存拷贝量, 从而易于发现意外的内存拷贝。特别是因为跨越Python和底层库的边界导致的意外 (例如:意外的把 numpy 数组转化成了Python数组,反之亦然)。

安装

Scalene 通过 pip 包的形式进行分发,可以运行在Mac OS X和Linux平台(包括在Windows WSL2中运行的Ubuntu)。

你可以通过下面的方式安装:


        
% pip install scalene
    

或者


        
% python -m pip install scalene
    

注意: 现在这样安装Scalene,是不会安装内存分析的库,所以你只能用它来执行CPU的分析。如果要使用它的内存分析能力,你需要下载这个代码仓库。

NEW: 你现在可以通过以下命令,在 Mac OS X 上使用 brew 安装内存分析的部分:


        
% brew tap emeryberger/scalene  
  % brew install --head libscalene
    

这将会安装一个你可以使用的 scalene 脚本(下面会提到)。

使用

下面的命令会让 Scalene 在提供的示例程序上执行 行级别的CPU分析。


        
% python -m scalene test/testme.py
    

如果你使用Homebrew安装 Scalene 库,你只需要执行 scalene 就可以执行行级别的CPU和内存分析:


        
% scalene test/testme.py
    

否则,你需要运行 make 来先构建一个指定的内存分配器:


        
% make
    

在 Mac OS X 系统上进行分析(不使用Homebrew安装):


        
% DYLD_INSERT_LIBRARIES=$PWD/libscalene.dylib PYTHONMALLOC=malloc python -m scalene test/testme.py
    

在Linux系统上分析:


        
% LD_PRELOAD=$PWD/libscalene.so PYTHONMALLOC=malloc python -m scalene test/testme.py
    

执行时增加 --help 来查看全部配置:


        
% python3 -m scalene --help  
usage: scalene [-h] [-o OUTFILE] [--profile-interval PROFILE_INTERVAL]  
               [--wallclock]  
               prog  
  
Scalene: a high-precision CPU and memory profiler.  
            https://github.com/emeryberger/Scalene  
  
                for CPU profiling only:  
            % python -m scalene yourprogram.py  
                for CPU and memory profiling (Mac OS X):  
            % DYLD_INSERT_LIBRARIES=$PWD/libscalene.dylib PYTHONMALLOC=malloc python -m scalene yourprogram.py  
                for CPU and memory profiling (Linux):  
            % LD_PRELOAD=$PWD/libscalene.so PYTHONMALLOC=malloc python -m scalene yourprogram.py  
  
positional arguments:  
  prog                  program to be profiled  
  
optional arguments:  
  -h, --help            show this help message and exit  
  -o OUTFILE, --outfile OUTFILE  
                        file to hold profiler output (default: stdout)  
  --profile-interval PROFILE_INTERVAL  
                        output profiles every so many seconds.  
  --wallclock           use wall clock time (default: virtual time)
    

对比其他分析器

性能和功能

下面的表格把 scalene 和不同分析器的性能做了比较。运行的示例程序 (benchmarks/julia1_nopil.py) 来自于 Gorelick 和 Ozsvald 的 《高性能Python编程》。所有的这些结果都是在 2016款 MacBook Pro上运行的。

picture.image

这个表格是其他分析器 vs. Scalene 的功能比较。

picture.image

输出

Scalene 打印被分析程序中带注释的源代码,以及程序在同目录和子目录使用到的任何模块。下面是一个来自 pystone.py pystone.py 的片段,只使用了CPU分析:


        
benchmarks/pystone.py: % of CPU time = 100.00% out of   3.66s.  
             |     CPU % |     CPU % |  
      Line   |  (Python) |  (native) |  [benchmarks/pystone.py]  
    --------------------------------------------------------------------------------  
    [... lines omitted ...]  
       137   |     0.27% |     0.14% | def Proc1(PtrParIn):  
       138   |     1.37% |     0.11% |     PtrParIn.PtrComp = NextRecord = PtrGlb.copy()  
       139   |     0.27% |     0.22% |     PtrParIn.IntComp = 5  
       140   |     1.37% |     0.77% |     NextRecord.IntComp = PtrParIn.IntComp  
       141   |     2.47% |     0.93% |     NextRecord.PtrComp = PtrParIn.PtrComp  
       142   |     1.92% |     0.78% |     NextRecord.PtrComp = Proc3(NextRecord.PtrComp)  
       143   |     0.27% |     0.17% |     if NextRecord.Discr == Ident1:  
       144   |     0.82% |     0.30% |         NextRecord.IntComp = 6  
       145   |     2.19% |     0.79% |         NextRecord.EnumComp = Proc6(PtrParIn.EnumComp)  
       146   |     1.10% |     0.39% |         NextRecord.PtrComp = PtrGlb.PtrComp  
       147   |     0.82% |     0.06% |         NextRecord.IntComp = Proc7(NextRecord.IntComp, 10)  
       148   |           |           |     else:  
       149   |           |           |         PtrParIn = NextRecord.copy()  
       150   |     0.82% |     0.32% |     NextRecord.PtrComp = None  
       151   |           |           |     return PtrParIn
    

下面是一个启用了内存分析的示例,运行的是Julia的基准测试。第一行是一个“sparkline”,总结了一段时间内的内存消耗。


        
Memory usage: ▁▁▄▇█▇▇▇█▇█▇█▇█▇█▇▇▇▇█▇▇█▇█▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇▇█ (max: 105.73MB)  
    benchmarks/julia1_nopil.py: % of CPU time = 100.00% out of   9.11s.  
             |     CPU % |     CPU % | Avg memory  | Memory      |  
      Line   |  (Python) |  (native) | growth (MB) | usage (%)   | [benchmarks/julia1_nopil.py]  
    --------------------------------------------------------------------------------  
         1   |           |           |             |             | import sys  
    [... lines omitted ...]  
        30   |           |           |             |             | def calculate_z_serial_purepython(maxiter, zs, cs):  
        31   |           |           |             |             |     """Calculate output list using Julia update rule"""  
        32   |           |           |          18 |       0.74% |     output = [0] * len(zs)  
        33   |     0.44% |     0.06% |          16 |       1.32% |     for i in range(len(zs)):  
        34   |           |           |             |             |         n = 0  
        35   |     0.22% |     0.04% |         -16 |             |         z = zs[i]  
        36   |     0.22% |     0.07% |             |             |         c = cs[i]  
        37   |    26.12% |     5.57% |             |             |         while abs(z) < 2 and n < maxiter:  
        38   |    36.04% |     7.74% |          16 |      85.09% |             z = z * z + c  
        39   |    12.01% |     2.70% |         -16 |       3.96% |             n += 1  
        40   |     0.33% |     0.10% |             |             |         output[i] = n  
        41   |           |           |             |             |     return output  
        42   |           |           |             |             |
    

正的内存数代表内存的分配量(以MB为单位),负的内存数代表内存的回收量。内存的使用率代表特定行中总内存分配的活动。

致谢

Logo由 Sophia Berger 创作。

原文标题:scalene: a high-performance CPU and memory profiler for Python

原文链接:github.com/emeryberger/

推荐阅读:

骚操作!Pandas还能用来写爬虫?

老司机教你5分钟读懂Python装饰器

用Python实现粒子群算法

2020Python招聘内推渠道开启啦!

picture.image

picture.image

▼点击阅读原文 立即秒杀云产品!

0
0
0
0
关于作者
关于作者

文章

0

获赞

0

收藏

0

相关资源
字节跳动云原生降本增效实践
本次分享主要介绍字节跳动如何利用云原生技术不断提升资源利用效率,降低基础设施成本;并重点分享字节跳动云原生团队在构建超大规模云原生系统过程中遇到的问题和相关解决方案,以及过程中回馈社区和客户的一系列开源项目和产品。
相关产品
评论
未登录
看完啦,登录分享一下感受吧~
暂无评论