热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

《Python高性能编程》——2.4计时的简单方法——打印和修饰

本节书摘来自异步社区《Python高性能编程》一书中的第2章,第2.4节,作者[美]戈雷利克(MichaGorelick)&

本节书摘来自异步社区《Python高性能编程》一书中的第2章,第2.4节,作者[美] 戈雷利克 (Micha Gorelick),胡世杰,徐旭彬 译,更多章节内容可以访问云栖社区“异步社区”公众号查看。

2.4 计时的简单方法——打印和修饰

运行例2-4,我们看到的输出是由代码中几句print语句生成的。在Ian的笔记本上用CPython 2.7跑这段代码要花大约12秒。运行时间一般都会有一些波动。你必须在计时的同时观察这些正常的变化,否则你可能会误把某个随机的运行时间的变化当作是由于某次代码的改进造成的。

你的计算机在运行你的代码时还会进行其他任务,比如访问网络、磁盘或RAM,这些因素都会导致程序运行时间的变化。

Ian的笔记本是一台Dell E6420,拥有一个Intel Core I7-2720QM的CPU(2.20GHz,6MB缓存,4核)以及8GB的RAM,操作系统是Ubuntu 13.10。

在calc_pure_python中(例2-2),我们能看到一些print语句。这是最简单的在函数内部测量一段代码执行时间的方法。这个基本方法既快且脏,是在你刚开始着手调查代码时非常有用的手段。

在代码除错和性能分析上使用print语句是常用的手段。虽然它很快就会变得无法管理,但适用于简短的调查。用完它们以后要记得收拾干净,否则它们会搞乱你的stdout。

一个稍微干净一点的方法是使用修饰器——在需要调查的函数上面增加一行代码。我们的修饰器十分简单,仅仅复制了print语句的功能。后面我们会让它变得更加高级。

在例2-5中,我们定义了一个新函数timefn,它以一个函数fn为参数。它的内嵌函数measure_time接受args(数量可变的位置参数)以及*kwargs(数量可变的键值对参数)等参数并将其传入fn执行。在执行fn前后,我们抓取time.time()并将结果和fn.func_name一起打印出来。使用这个修饰器的开销很小,但如果你调用上千万次fn,开销就会变得引人注意。我们用@wraps(fn)将函数名和docstring暴露给fn的调用者(否则调用者看到的将是修饰器自身的函数名和docstring,而不是被修饰的函数的)。

例2-5 定义一个修饰器来自动测量时间

from functools import wrapsdef timefn(fn):@wraps(fn)def measure_time(*args, **kwargs):t1 = time.time()result = fn(*args, **kwargs)t2 = time.time()print ("@timefn:" + fn.func_name + " took " + str(t2 - t1) + " seconds")return resultreturn measure_time@timefn
def calculate_z_serial_purepython(maxiter, zs, cs):...

当我们运行这个版本的代码时(之前的print语句依然保留),我们会看到修饰器打印的执行时间要略快于calc_pure_python打印的时间。这是由于函数的调用带来了额外开销(差异非常小):

Length of x: 1000
Total elements: 1000000
@timefn:calculate_z_serial_purepython took 12.2218790054 seconds
calculate_z_serial_purepython took 12.2219250043 seconds

 备忘 额外的分析信息不可避免地降低了代码的执行速度——某些性能分析选项非常详细以至于带来了巨大的性能代价。分析信息的细节程度和运行速度是你必须要进行权衡的。

我们可以用timeit模块作为另一种测量执行速度的方法。通常来说,你会在解决问题的过程中用它来为各种简单的语句计时。

 警告  注意,timeit模块暂时禁用了垃圾收集器。如果你的操作会调用到垃圾收集器,那么它有可能影响到你实际操作的速度。更多信息请参见Python文档:http://bit.ly/timeit_doc。

你可以从命令行运行timeit如下:

$ python -m timeit -n 5 -r 5 -s "import julia1""julia1.calc_pure_python(desired_width=1000,max_iterations=300)"

注意你必须以-s命令在设置阶段导入julia1模块,因为calc_pure_python来自那个模块。timeit有一些合理的默认值适用于一段简短的代码,但对于要长期运行的代码来说,最好指定循环次数(-n 5)以及重复次数(-r 5)。timeit会对语句循环执行n次并计算平均值作为一个结果,重复r次并选出最好的那个结果。

如果我们不指定-n和-r运行timeit,默认是循环10次重复5次,需要6分钟。改变默认值可以让你更快获得结果。

我们只关注最好的那个结果,平均值以及最差结果可能是由于其他进程的影响。选择循环5次重复5次应该能给我们一个较为公正的结果:

5 loops, best of 5: 13.1 sec per loop

试着多运行几次,看看我们会不会得到不同的结果——你可能需要更多的重复次数来获得一个稳定的最佳结果时间。在这一点上不存在“正确”的配置,所以如果你发现你的计时结果变动范围很广,你就要选择更高的重复次数,直到获得稳定的最终结果。

我们的结果显示调用calc_pure_python的总体开销是13.1秒(最佳情况),而@timefn修饰器测算的单次调用calculate_z_serial_purepython耗时12.2秒。中间的差别主要是用于创建zs和cs列表的时间。

在IPython内部,我们可以用同样的方式使用%timeit魔法函数。如果你在IPython中用互动的方式开发你的代码且函数在本地名字空间(可能是因为你正在使用%run),那么你可以用:

%timeit calc_pure_python(desired_width=1000, max_iterations=300)

还有一点值得考虑的是一台计算机上的负载变化。很多后台运行的任务(如Dropbox、备份等)都会随机影响CPU和磁盘资源。网页上的脚本也会导致不可预测的资源使用。图2-4显示了我们刚刚进行的计时过程中某个CPU的使用率达到了100%,机器中的其他核心都在轻松处理其他的任务。


screenshot

系统监视器会时不时地显示这台机器上的活动峰值。有必要检查会不会有其他事件发生影响了你的关键资源(CPU、磁盘、网络)。



推荐阅读
  • 开发笔记:python协程的理解
    篇首语:本文由编程笔记#小编为大家整理,主要介绍了python协程的理解相关的知识,希望对你有一定的参考价值。一、介绍什么是并发?并发的本质就是 ... [详细]
  • 【系统架构师精讲】(16):操作系统核心概念——寄存器、内存与缓存机制详解
    在计算机系统架构中,中央处理器(CPU)内部集成了多种高速存储组件,用于临时存储指令、数据和地址。这些组件包括指令寄存器(IR)、程序计数器(PC)和累加器(ACC)。寄存器作为集成电路中的关键存储单元,由触发器构成,具备极高的读写速度,使得数据传输非常迅速。根据功能不同,寄存器可分为基本寄存器和移位寄存器,各自在数据处理中发挥重要作用。此外,寄存器与内存和缓存机制的协同工作,确保了系统的高效运行。 ... [详细]
  • 在《Linux高性能服务器编程》一书中,第3.2节深入探讨了TCP报头的结构与功能。TCP报头是每个TCP数据段中不可或缺的部分,它不仅包含了源端口和目的端口的信息,还负责管理TCP连接的状态和控制。本节内容详尽地解析了TCP报头的各项字段及其作用,为读者提供了深入理解TCP协议的基础。 ... [详细]
  • 本文探讨了如何通过编程手段在Linux系统中禁用硬件预取功能。基于Intel® Core™微架构的应用性能优化需求,文章详细介绍了相关配置方法和代码实现,旨在帮助开发人员有效控制硬件预取行为,提升应用程序的运行效率。 ... [详细]
  • 使用Maven JAR插件将单个或多个文件及其依赖项合并为一个可引用的JAR包
    本文介绍了如何利用Maven中的maven-assembly-plugin插件将单个或多个Java文件及其依赖项打包成一个可引用的JAR文件。首先,需要创建一个新的Maven项目,并将待打包的Java文件复制到该项目中。通过配置maven-assembly-plugin,可以实现将所有文件及其依赖项合并为一个独立的JAR包,方便在其他项目中引用和使用。此外,该方法还支持自定义装配描述符,以满足不同场景下的需求。 ... [详细]
  • 利用ZFS和Gluster实现分布式存储系统的高效迁移与应用
    本文探讨了在Ubuntu 18.04系统中利用ZFS和Gluster文件系统实现分布式存储系统的高效迁移与应用。通过详细的技术分析和实践案例,展示了这两种文件系统在数据迁移、高可用性和性能优化方面的优势,为分布式存储系统的部署和管理提供了宝贵的参考。 ... [详细]
  • 在 Vue 应用开发中,页面状态管理和跨页面数据传递是常见需求。本文将详细介绍 Vue Router 提供的两种有效方式,帮助开发者高效地实现页面间的数据交互与状态同步,同时分享一些最佳实践和注意事项。 ... [详细]
  • 手指触控|Android电容屏幕驱动调试指南
    手指触控|Android电容屏幕驱动调试指南 ... [详细]
  • 本文深入解析了Java 8并发编程中的`AtomicInteger`类,详细探讨了其源码实现和应用场景。`AtomicInteger`通过硬件级别的原子操作,确保了整型变量在多线程环境下的安全性和高效性,避免了传统加锁方式带来的性能开销。文章不仅剖析了`AtomicInteger`的内部机制,还结合实际案例展示了其在并发编程中的优势和使用技巧。 ... [详细]
  • 并发编程入门:初探多任务处理技术
    并发编程入门:探索多任务处理技术并发编程是指在单个处理器上高效地管理多个任务的执行过程。其核心在于通过合理分配和协调任务,提高系统的整体性能。主要应用场景包括:1) 将复杂任务分解为多个子任务,并分配给不同的线程,实现并行处理;2) 通过同步机制确保线程间协调一致,避免资源竞争和数据不一致问题。此外,理解并发编程还涉及锁机制、线程池和异步编程等关键技术。 ... [详细]
  • 在MySQL中实现时间比较功能的详细解析与应用
    在MySQL中实现时间比较功能的详细解析与应用。本文深入探讨了MySQL中时间比较的实现方法,重点介绍了`UNIX_TIMESTAMP`函数的应用。该函数可以接收一个日期时间参数,也可以不带参数使用,其返回值为Unix时间戳,便于进行时间的精确比较和计算。此外,文章还涵盖了其他相关的时间处理函数和技巧,帮助读者更好地理解和掌握MySQL中的时间操作。 ... [详细]
  • 提升Python多环境管理效率:深入探索多Python Pip应用策略
    提升Python多环境管理效率:深入探索多Python Pip应用策略 ... [详细]
  • 为什么python是动态类型语言_Python 3.7.0 面向对象的动态类型语言
    代表Python开发社区和Python3.7发布团队,我们很高兴地宣布https:www.python.orgdownloadsreleasepython-370 ... [详细]
  • 开发笔记:Python之路第一篇:初识Python
    篇首语:本文由编程笔记#小编为大家整理,主要介绍了Python之路第一篇:初识Python相关的知识,希望对你有一定的参考价值。Python简介& ... [详细]
  • 在《Python编程基础》课程中,我们将深入探讨Python中的循环结构。通过详细解析for循环和while循环的语法与应用场景,帮助初学者掌握循环控制语句的核心概念和实际应用技巧。此外,还将介绍如何利用循环结构解决复杂问题,提高编程效率和代码可读性。 ... [详细]
author-avatar
Y死一般的痛过
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有