NumPy 秘籍中文第二版:七、性能分析和调试

news2025/3/1 13:58:26

原文:NumPy Cookbook - Second Edition

协议:CC BY-NC-SA 4.0

译者:飞龙

在本章中,我们将介绍以下秘籍:

  • 使用timeit进行性能分析
  • 使用 IPython 进行分析
  • 安装line_profiler
  • 使用line_profiler分析代码
  • 具有cProfile扩展名的性能分析代码
  • 使用 IPython 进行调试
  • 使用PuDB进行调试

简介

调试是从软件中查找和删除错误的行为。 分析是指构建程序的概要文件,以便收集有关内存使用或时间复杂度的信息。 分析和调试是开发人员生活中必不可少的活动。 对于复杂的软件尤其如此。 好消息是,许多工具可以为您提供帮助。 我们将回顾 NumPy 用户中流行的技术。

使用timeit进行性能分析

timeit是一个模块,可用于计时代码段。 它是标准 Python 库的一部分。 我们将使用几种数组大小对sort() NumPy 函数计时。 经典的快速排序归并排序算法的平均运行时间为O(N log N),因此我们将尝试将这个模型拟合到结果。

操作步骤

我们将要求数组进行排序:

  1. 创建数组以排序包含随机整数值的各种大小:

    times = np.array([])
    
    for size in sizes:
        integers = np.random.random_integers (1, 10 ** 6, size)
    
  2. 要测量时间,请创建一个计时器,为其提供执行函数,并指定相关的导入。 然后,排序 100 次以获取有关排序时间的数据:

    def measure():
        timer = timeit.Timer('dosort()', 'from __main__ import dosort')
        return timer.timeit(10 ** 2)
    
  3. 通过一次乘以时间来构建测量时间数组:

    times = np.append(times, measure())
    
  4. 将时间拟合为n log n的理论模型。 由于我们将数组大小更改为 2 的幂,因此很容易:

    fit = np.polyfit(sizes * powersOf2, times, 1)
    

    以下是完整的计时代码:

    import numpy as np
    import timeit
    import matplotlib.pyplot as plt
    
    # This program measures the performance of the NumPy sort function
    # and plots time vs array size.
    integers = []
    
    def dosort():
       integers.sort()
    
    def measure():
       timer = timeit.Timer('dosort()', 'from __main__ import dosort')
    
       return timer.timeit(10 ** 2)
    
    powersOf2 = np.arange(0, 19)
    sizes = 2 ** powersOf2
    
    times = np.array([])
    
    for size in sizes:
       integers = np.random.random_integers(1, 10 ** 6, size)
       times = np.append(times, measure())
    
    fit = np.polyfit(sizes * powersOf2, times, 1)
    print(fit)
    plt.title("Sort array sizes vs execution times")
    plt.xlabel("Size")
    plt.ylabel("(s)")
    plt.semilogx(sizes, times, 'ro')
    plt.semilogx(sizes, np.polyval(fit, sizes * powersOf2))
    plt.grid()
    plt.show()
    

    以下屏幕截图显示了运行时间与数组大小的关系图:

    How to do it...

工作原理

我们测量了sort() NumPy 函数的平均运行时间。 此秘籍中使用了以下函数:

函数描述
random_integers()给定值和数组大小的范围时,此函数创建一个随机整数数组
append()此函数将值附加到 NumPy 数组
polyfit()此函数将数据拟合为给定阶数的多项式
polyval()此函数计算多项式,并为给定的 x值返回相应的值
semilogx()此函数使用对数刻度在 X 轴上绘制数据

另见

  • timeit的文档

使用 IPython 进行分析

在 IPython 中,我们可以使用timeit来分析代码的小片段。 我们可以也分析较大的脚本。 我们将展示两种方法。

操作步骤

首先,我们将介绍一个小片段:

  1. pylab模式启动 IPython:

    $ ipython --pylab
    

    创建一个包含 1000 个介于 0 到 1000 之间的整数值的数组:

    In [1]: a = arange(1000)
    

    测量在数组中搜索“所有问题的答案”(42)所花费的时间。 是的,所有问题的答案都是 42。如果您不相信我,请阅读这个页面:

    In [2]: %timeit searchsorted(a, 42)
    100000 loops, best of 3: 7.58 us per loop
    
  2. 剖析以下小脚本,该小脚本可以反转包含随机值的大小可变的矩阵。 NumPy 矩阵的.I属性(即大写I)表示该矩阵的逆:

    import numpy as np
    
    def invert(n):
      a = np.matrix(np.random.rand(n, n))
    
      return a.I
    
    sizes = 2 ** np.arange(0, 12)
    
    for n in sizes:
      invert(n)
    

    将此代码计时如下:

    In [1]: %run -t invert_matrix.py
    
    IPython CPU timings (estimated):
     User   :       6.08 s.
     System :       0.52 s.
    Wall time:      19.26 s.
    
    

    然后使用p选项对脚本进行配置:

    In [2]: %run -p invert_matrix.py
    
    852 function calls in 6.597 CPU seconds
    
       Ordered by: internal time
    
       ncalls  tottime  percall  cumtime  percall filename:lineno(function)
           12    3.228    0.269    3.228    0.269 {numpy.linalg.lapack_lite.dgesv}
           24    2.967    0.124    2.967    0.124 {numpy.core.multiarray._fastCopyAndTranspose}
           12    0.156    0.013    0.156    0.013 {method 'rand' of 'mtrand.RandomState' objects}
           12    0.087    0.007    0.087    0.007 {method 'copy' of 'numpy.ndarray' objects}
           12    0.069    0.006    0.069    0.006 {method 'astype' of 'numpy.ndarray' objects}
           12    0.025    0.002    6.304    0.525 linalg.py:404(inv)
           12    0.024    0.002    6.328    0.527 defmatrix.py:808(getI)
            1    0.017    0.017    6.596    6.596 invert_matrix.py:1(<module>)
           24    0.014    0.001    0.014    0.001 {numpy.core.multiarray.zeros}
           12    0.009    0.001    6.580    0.548 invert_matrix.py:3(invert)
           12    0.000    0.000    6.264    0.522 linalg.py:244(solve)
           12    0.000    0.000    0.014    0.001 numeric.py:1875(identity)
            1    0.000    0.000    6.597    6.597 {execfile}
           36    0.000    0.000    0.000    0.000 defmatrix.py:279(__array_finalize__)
           12    0.000    0.000    2.967    0.247 linalg.py:139(_fastCopyAndTranspose)
           24    0.000    0.000    0.087    0.004 defmatrix.py:233(__new__)
           12    0.000    0.000    0.000    0.000 linalg.py:99(_commonType)
           24    0.000    0.000    0.000    0.000 {method '__array_prepare__' of 'numpy.ndarray' objects}
           36    0.000    0.000    0.000    0.000 linalg.py:66(_makearray)
           36    0.000    0.000    0.000    0.000 {numpy.core.multiarray.array}
           12    0.000    0.000    0.000    0.000 {method 'view' of 'numpy.ndarray' objects}
           12    0.000    0.000    0.000    0.000 linalg.py:127(_to_native_byte_order)
            1    0.000    0.000    6.597    6.597 interactiveshell.py:2270(safe_execfile)
    

工作原理

我们通过分析器运行了上述 NumPy 代码。 下表概述了分析器的输出:

函数描述
ncalls这是调用次数
tottime这是一个函数花费的总时间
percall这是每次通话所花费的时间 ,计算方法是将总时间除以通话次数
cumtime这是在函数和由函数调用的函数(包括递归调用)上花费的累积时间

另见

  • IPython 魔术文档

安装line_profiler

line_profiler由 NumPy 的开发人员之一创建。 此模块对 Python 代码进行逐行分析。 我们将在此秘籍中描述必要的安装步骤。

准备

您可能需要安装setuptools。 先前的秘籍中对此进行了介绍; 如有必要,请参阅“另见”部分。 为了安装开发版本,您将需要 Git。 安装 Git 超出了本书的范围。

操作步骤

选择适合您的安装选项:

  • 使用以下任一命令将line_profilereasy_install一起安装:

    $ easy_install line_profiler
    $ pip install line_profiler
    
    
  • 安装开发版本。

    使用 Git 查看源代码:

    $ git clone https://github.com/rkern/line_profiler
    
    

    签出源代码后,按如下所示构建它:

    $ python setup.py install
    
    

另见

  • 第 1 章,“使用 IPython”中的“安装 IPython”

使用line_profiler分析代码

现在我们已经安装完毕,可以开始分析。

操作步骤

显然,我们将需要代码来分析:

  1. 编写以下代码,以自身乘以大小可变的随机矩阵。 此外,线程将休眠几秒钟。 使用@profile注解函数以进行概要分析:

    import numpy as np
    import time
    
    @profile
    def multiply(n):
      A = np.random.rand(n, n)
      time.sleep(np.random.randint(0, 2))
      return np.matrix(A) ** 2
    
    for n in 2 ** np.arange(0, 10):
      multiply(n)
    
  2. 使用以下命令运行事件分析器:

    $ kernprof.py -l -v mat_mult.py
    Wrote profile results to mat_mult.py.lprof
    Timer unit: 1e-06 s
    
    File: mat_mult.py
    Function: multiply at line 4
    Total time: 3.19654 s
    
    Line #      Hits         Time  Per Hit   % Time  Line Contents
    ==============================================================
     4                                           @profile
     5                                           def multiply(n):
     6        10        13461   1346.1      0.4     A = numpy.random.rand(n, n)
     7        10      3000689 300068.9     93.9     time.sleep(numpy.random.randint(0, 2))
     8        10       182386  18238.6      5.7     return numpy.matrix(A) ** 2
    
    

工作原理

@profile装饰器告诉line_profiler要分析哪些函数。 下表说明了分析器的输出:

函数描述
Line #文件中的行号
Hits执行该行的次数
Time执行该行所花费的时间
Per Hit执行该行所花费的平均时间
% Time执行该行所花费的时间相对于执行所有行所花费的时间的百分比
Line Contents该行的内容

另见

  • Github line_profiler项目页面

cProfile扩展和代码性能分析

cProfile是 Python 2.5 中引入的C扩展名。 它可以用于确定性分析。 确定性分析表示所获得的时间测量是精确的,并且不使用采样。 这与统计分析相反,统计分析来自随机样本。 我们将使用cProfile对一个小的 NumPy 程序进行分析,该程序会对具有随机值的数组进行转置。

操作步骤

同样,我们需要代码来配置:

  1. 编写以下transpose()函数以创建具有随机值的数组并将其转置:

    def transpose(n):
      random_values = np.random.random((n, n))
      return random_values.T
    
  2. 运行分析器,并为其提供待分析函数:

    cProfile.run('transpose (1000)')
    

    可以在以下片段中找到本教程的完整代码:

    import numpy as np
    import cProfile
    
    def transpose(n):
       random_values = np.random.random((n, n))
       return random_values.T
    
    cProfile.run('transpose (1000)')
    

    对于1000 x 1000的数组,我们得到以下输出:

    4 function calls in 0.029 CPU seconds
     Ordered by: standard name
     ncalls  tottime  percall  cumtime  percall filename:lineno(function)
     1    0.001    0.001    0.029    0.029 <string>:1(<module>)
     1    0.000    0.000    0.028    0.028 cprofile_transpose.py:5(transpose)
     1    0.000    0.000    0.000    0.000 {method 'disable' of '_lsprof.Profiler' objects}
     1    0.028    0.028    0.028    0.028 {method 'random_sample' of 'mtrand.RandomState' objects}
    
    

    输出中的列与 IPython 分析秘籍中看到的列相同。

另见

  • Python 分析器文档
  • pstats一起工作的教程

使用 IPython 进行调试

“如果调试是清除软件错误的过程,则编程必须是放入它们的过程。”

– 荷兰计算机科学家 Edsger Dijkstra,1972 年图灵奖的获得者

调试是没人真正喜欢,但是掌握这些东西非常重要的东西之一。 这可能需要几个小时,并且由于墨菲定律,您很可能没有时间。 因此,重要的是要系统地了解您的工具。 找到错误并实现修复后,您应该进行单元测试(如果该错误具有来自问题跟踪程序的相关 ID,我通常在末尾附加 ID 来命名测试)。 这样,您至少不必再次进行调试。 下一章将介绍单元测试。 我们将调试以下错误代码。 它尝试访问不存在的数组元素:

import numpy as np

a = np.arange(7)
print(a[8])

IPython 调试器充当普通的 Python pdb调试器; 它添加了选项卡补全和语法突出显示等功能。

操作步骤

以下步骤说明了典型的调试会话:

  1. 启动 IPython Shell。 通过发出以下命令在 IPython 中运行错误脚本:

    In [1]: %run buggy.py
    ---------------------------------------------------------------------------
    IndexError                                Traceback (most recent call last)
    .../site-packages/IPython/utils/py3compat.pyc in execfile(fname, *where)
     173             else:
     174                 filename = fname
    --> 175             __builtin__.execfile(filename, *where)
    
    .../buggy.py in <module>()
     2
     3 a = numpy.arange(7)
    ----> 4 print a[8]
    
    IndexError: index out of bounds
    
    
  2. 现在您的程序崩溃了,启动调试器。 在发生错误的行上设置一个断点:

    In [2]: %debug
    > .../buggy.py(4)<module>()
     2 
     3 a = numpy.arange(7)
    ----> 4 print a[8]
    
    
  3. 使用list命令列出代码,或使用简写l

    ipdb> list
     1 import numpy as np
     2 
     3 a = np.arange(7)
    ----> 4 print(a[8])
    
    
  4. 现在,我们可以在调试器当前所在的行上求值任意代码:

    ipdb> len(a)
    7
    
    ipdb> print(a)
    [0 1 2 3 4 5 6]
    
    
  5. 调用栈是包含有关正在运行的程序的活动函数的信息的栈。 使用bt命令查看调用栈:

    ipdb> bt
     .../py3compat.py(175)execfile()
     171             if isinstance(fname, unicode):
     172                 filename = fname.encode(sys.getfilesystemencoding())
     173             else:
     174                 filename = fname
    --> 175             __builtin__.execfile(filename, *where)
    
    > .../buggy.py(4)<module>()
     0 print a[8]
    
    

    向上移动调用栈:

    ipdb> u
    > .../site-packages/IPython/utils/py3compat.py(175)execfile()
     173             else:
     174                 filename = fname
    --> 175             __builtin__.execfile(filename, *where)
    
    

    下移调用栈:

    ipdb> d
    > .../buggy.py(4)<module>()
     2
     3 a = np.arange(7)
    ----> 4 print(a[8])
    
    

工作原理

在本教程中,您学习了如何使用 IPython 调试 NumPy 程序。 我们设置一个断点并导航调用栈。 使用了以下调试器命令:

函数描述
listl列出源代码
bt显示调用栈
u向上移动调用栈
d下移调用栈

另见

  • Python 调试器文档
  • ipdb 包的主页

使用 PuDB 进行调试

PuDB 是基于视觉的,全屏,基于控制台的 Python 调试器,易于安装。 PuDB 支持光标键和 vi 命令。 如果需要,我们还可以将此调试器与 IPython 集成。

操作步骤

我们将从安装pudb开始:

  1. 要安装pudb,我们只需执行以下命令(或等效的pip命令):

    $ sudo easy_install pudb
    $ pip install pudb
    $ pip freeze|grep pudb
    pudb==2014.1
    
    
  2. 让我们调试前面示例中的buggy程序。 如下所示启动调试器:

    $ python -m pudb buggy.py
    
    

    以下屏幕截图显示了调试器的用户界面:

    How to do it...

屏幕快照在顶部显示了最重要的调试命令。 我们还可以看到正在调试的代码,变量,栈和定义的断点。 键入q退出大多数菜单。 键入n将调试器移至下一行。 我们还可以使用光标键或 vi 的jk键移动,例如,通过键入b设置断点。

另见

  • PyPi PuDB 页面

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/412441.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【分布式事务AT模式 本地部署Seata服务】分布式事务框架Seata详细讲解

前言 这篇文章我会从0到1详细搭建分布式事务框架seata的使用&#xff0c;那么我们首先要先了解一下什么是分布式事务&#xff1f; 本篇文章是本地启动seata服务并且注册到nacos中&#xff0c;在SpringCloud中整合seata框架请转移下方连接 点我跳转SpringCloud整合seata教程&…

【网络原理】TCP/IP协议(续)

目录 &#x1f525;网络层重点协议&#xff08;IP 协议&#xff09; 一、地址管理 1.如何解决上述地址不够用问题&#xff1f; 2.NAT 机制 2.1 NAPT 2.2 在 NAT 背景下如何通信&#xff1f; 3.IPv6 4.IP地址 4.1 ABCDE类 4.2 子网掩码 4.3 特殊的 IP 地址 二、路由…

传统汽车保险丝盒与智能保险丝盒Efuse的应用

一、传统汽车保险丝盒 1、概述 电气盒是用于提供车辆电源分配和回路保护的电气枢纽。电气盒能简化线束的安装和整车的装配过程&#xff0c;改善系统的整体质量水平&#xff0c;降低成本和减少散乱。 一般传统电气盒分为PFB&#xff08;预保险丝盒&#xff09;&#xff0c;UE…

公网使用SSH远程登录macOS服务器【内网穿透】

文章目录前言1. macOS打开远程登录2. 局域网内测试ssh远程3. 公网ssh远程连接macOS3.1 macOS安装配置cpolar3.2 获取ssh隧道公网地址3.3 测试公网ssh远程连接macOS4. 配置公网固定TCP地址4.1 保留一个固定TCP端口地址4.2 配置固定TCP端口地址5. 使用固定TCP端口地址ssh远程前言…

Nacos共享配置

本文介绍一下Nacos作为配置中心时&#xff0c;如何读取共享配置 我的环境 Windows10JDK8SpringCloud&#xff1a;Finchley.RELEASESpringBoot&#xff1a;2.0.4.RELEASEspring-cloud-alibaba-dependencies&#xff1a;0.2.2.RELEASENacos-server&#xff1a;1.0.1 本文的项目…

去互联网大厂卷还是去上升期创业型公司offer二选一?你怎么抉择?

上升期的创业型公司 vs 大厂 如何抉择&#xff1f; 最近总有一些粉丝特别“凡尔赛”的发几个 offer 问我选择哪个&#xff1f;其中比较典型的一个问题就是&#xff1a; “一个是处于上升期的创业型公司 &#xff0c;一个行业大厂&#xff0c;薪资待遇差不多&#xff0c;到底该…

elastissearch——排序结果处理

排序 elasticsearch支持对搜索结果排序&#xff0c;默认是根据相关度算分&#xff08;_score&#xff09;来排序。可以排序字段类型有&#xff1a;keyword类型、数值类型、地理坐标类型、日期类型等。 GET /hotel/_search { "query": { "match_all"…

DC插装式流量阀压力阀

Cartridge Valves 电磁阀 止回阀 运动控制阀 流量控制阀 溢流阀 压力控制阀 顺序阀 梭阀 方向阀 配件 Zero Profile Valves 止回阀 运动控制阀 流量控制阀 溢流阀 梭阀 In-Line Valves 止回阀和梭阀 方向阀 配件 微型系列 AB20S APIDC-30S C10B C10S C10S…

opengl 坐标系

概述 为了将坐标从一个坐标系统转换成另一个坐标&#xff0c;我们需要经历几个变换&#xff08;1&#xff1a;模型 2&#xff1a;观察 3&#xff1a;投影&#xff09;我们的顶点坐标起始于局部坐标&#xff0c;然后变成世界坐标&#xff0c;观察坐标&#xff0c;剪裁坐标 最后以…

BUUCTF-MD5强弱比较-MD5()的万能密码-tornado框架注入-中文电码

第六周 第三次 目录 学习到的知识 1.MD5强弱比较可以都可以使用数组绕过 2.基于MD5()的万能密码 ffifdyop WEB [BJDCTF2020]Easy MD5 ​编辑[护网杯 2018]easy_tornado Crypto 信息化时代的步伐 凯撒&#xff1f;替换&#xff1f;呵呵! Misc 神秘龙卷风 学习到的…

c/c++:数据类型,常量变量,标识符,有符号整型,无符号unsigned,字符类型,字符串类型,实数类型,浮点型,科学计数法

c/c&#xff1a;数据类型&#xff0c;常量变量&#xff0c;标识符&#xff0c;有符号整型&#xff0c;无符号unsigned&#xff0c;字符类型&#xff0c;字符串类型&#xff0c;实数类型&#xff0c;浮点型&#xff0c;科学计数法 2022找工作是学历、能力和运气的超强结合体&am…

C++ Primer 第7章 类 - 上(零基础学习C++,精简学习笔记)

&#x1f916; 作者简介&#xff1a;努力的clz &#xff0c;一个努力编程的菜鸟 &#x1f423;&#x1f424;&#x1f425; &#x1f440; 文章专栏&#xff1a;C Primer 学习笔记 &#x1f4d4;专栏简介&#xff1a; 本专栏是博主学习 C Primer 的学习笔记&#xff0c;因为…

【NX2023/1847】UG软件安装详细指南教程

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录安装包一、安装包内容检查二、安装步骤1.安装JAVA_WIN64.exe2.运行Launch.exe3.安装许可3.直接重启电脑&#xff08;小白直接重启稳妥&#xff09;4.重启后继续运行L…

网络威胁情报项目:为什么仍然很疯狂

大约五年前&#xff0c;向首席信息安全官&#xff08; CISO&#xff09;询问他们的网络威胁情报 (CTI) 计划时&#xff0c;得到了两种截然不同的回答。 资源丰富的大型企业正在投资他们的威胁情报计划&#xff0c;目的是为了战术、运营和战略目的更好地实施它。 规模较小、资…

Day942.独立编译调试 -系统重构实战

独立编译调试 Hi&#xff0c;我是阿昌&#xff0c;今天学习记录的是关于独立编译调试的内容。 当组件做 独立的版本演进时&#xff0c;如果开发在本地每次修改代码时&#xff0c;都需要进行集成打包验证&#xff0c;反而会影响日常的开发效率。所以如果能够让组件独立进行编译…

Object方法

私人博客 许小墨のBlog —— 菜鸡博客直通车 系列文章完整版&#xff0c;配图更多&#xff0c;CSDN博文图片需要手动上传&#xff0c;因此文章配图较少&#xff0c;看不懂的可以去菜鸡博客参考一下配图&#xff01; 系列文章目录 前端系列文章——传送门 JavaScript系列文章—…

Node.js -- 模块化

1.模块化的基本概念 模块化是指解决一个复杂问题时&#xff0c;自顶向下逐层吧系统划分成若干模块的过程。对于整个系统来说&#xff0c;模块是可组合&#xff0c;分解和更换的单元。 将代码进行模块化拆分的好处&#xff1a; 提高代码的复用性提高代码的可维护性可以实现按…

元宇宙:新的数字模式——元宇宙会场

一、引言 元宇宙是一个充满无限可能的虚拟空间&#xff0c;人们可以在其中创建和参与各种虚拟场景和体验。元宇宙技术的兴起&#xff0c;为传统的会场提供了一个新的方向。元宇宙会场将线下会场的物理空间转化为虚拟空间&#xff0c;通过数字技术和互联网实现了人们在虚拟环境…

我的第一台电脑的故事

第一台电脑啊&#xff0c;多么遥远的故事了&#xff0c;又似乎就在眼前。今天重回往事&#xff0c;就简单记录一下吧。 &#x1f331;缘起 那是初一&#xff0c;至今已13年&#xff0c;遂觉遥远&#xff0c;而又是立志我学习的起点&#xff0c;至今还在校园&#xff0c;又觉就…

断开连接图的 BFS

在上一篇文章中,仅对特定顶点执行 BFS,即假设所有顶点都可以从起始顶点到达。但是在断开连接的图或所有顶点都无法访问的任何顶点的情况下,之前的实现将不会给出所需的输出,因此在这篇文章中,在 BFS 中进行了修改。 所有顶点都是可达的。因此,对于上图,简单的BFS就可以…