首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >如何系统地评估Python脚本的性能?

如何系统地评估Python脚本的性能?
EN

Software Engineering用户
提问于 2016-07-16 06:39:59
回答 5查看 7.5K关注 0票数 1

我如何知道我的代码是否运行得足够快?有没有一种可测量的方法来测试我的代码的速度和性能?

例如,我有一个脚本,它在使用Numpy计算统计数据时读取CSV文件和编写新的CSV文件。下面,我使用cProfiler来编写我的Python,但是在看到结果之后,接下来怎么办?在这种情况下,我可以看到方法意味着,从numpy减少,从csv和python列表的方法附加的方法占用了相当一部分时间。

我如何知道我的代码是否可以改进?

代码语言:javascript
复制
  python -m cProfile -s cumulative OBSparser.py
     176657699 function calls (176651606 primitive calls) in 528.419 seconds
  Ordered by: cumulative time
  ncalls  tottime  percall  cumtime  percall filename:lineno(function)
       1    0.003    0.003  528.421  528.421 OBSparser.py:1(<module>)
       1    0.000    0.000  526.874  526.874 OBSparser.py:45(start)
       1  165.767  165.767  526.874  526.874 OBSparser.py:48(parse)
 7638018    6.895    0.000  179.890    0.000 {method 'mean' of 'numpy.ndarray' objects}
 7638018   56.780    0.000  172.995    0.000 _methods.py:53(_mean)
 7628171   57.232    0.000   57.232    0.000 {method 'writerow' of '_csv.writer' objects}
 7700878   52.580    0.000   52.580    0.000 {method 'reduce' of 'numpy.ufunc' objects}
 7615219   50.640    0.000   50.640    0.000 {method 'astype' of 'numpy.ndarray' objects}
 7668436   28.595    0.000   36.853    0.000 _methods.py:43(_count_reduce_items)
15323753   31.503    0.000   31.503    0.000 {numpy.core.multiarray.array}
45751805   13.439    0.000   13.439    0.000 {method 'append' of 'list' objects}

有人能解释一下最佳做法吗?

EN

回答 5

Software Engineering用户

回答已采纳

发布于 2016-07-17 04:38:01

我如何知道我的代码是否运行得足够快?

这在很大程度上取决于您的用例--您的程序运行1.4小时,速度可能足够快,也可能不够快。如果这是一个一次性的过程,那么1.4小时并不多--花在优化上的任何时间都不值得投资。另一方面,如果这是一个应该运行的过程,例如每小时一次,显然值得找到一种更省时的方法。

有没有一种可测量的方法来测试我的代码的速度和性能?

是的,侧写-你已经做过了。这是个很好的开始。

接下来我该怎么办?

最佳做法包括:

  1. 衡量基线性能(在优化之前)
  2. 分析程序花费大部分时间的部分。
  3. 降低运行时复杂性(大O型)
  4. 检查并行计算的潜力
  5. 与基线性能比较

你已经做了1,所以让我们转到2。

分析

在您的示例中,程序将大部分时间用于在线OBSparser.py:48,其中三分之一的时间用于计算平均7638018次。

正如分析器输出所显示的,这是在ndarray上进行的,即使用numpy,而且在每次调用的基础上似乎不需要花费很多时间。快速计算证实:

179‘/ 7.638.018 =每次通话23.6微秒

因为这已经在C代码(numpy)中实现了,所以通过更改实际的mean代码(或者使用另一个库),您很可能没有什么办法来提高每次调用的性能。

然而,问自己几个问题:

  1. 如何减少对.mean()的调用数量?
  2. .mean()的调用能否更有效地实现?
  3. 能否将数据分组,并独立处理每一组?
  4. 更多问题

其他值得考虑的调用是对.astype() and reduce的调用,我只是为了说明而专注于.mean()

降低复杂度

不知道您的代码实际上是做什么的,这里是我的5分钱的细节,无论如何:

在2.对我的i7内核进行快速检查后发现,对于ndarray.mean()来说,要花费20微秒左右,这需要大约50个值。因此,我猜您的是分组值,然后调用每个组的.mean()。可能有更有效的方法--搜索numpy组的聚合性能或其中的一些变体,可能会找到一些有用的指针。

并行计算

在这里,我猜多处理不太可能是一个解决方案,因为您的计算似乎主要是CPU限制的,并且启动独立任务和交换数据的开销可能比好处更大。

然而,可能有一些使用SIMD-方法,即矢量化。再说一次,只是个预感。

与基线性能

的比较

为了减少重新配置文件所需的时间,请考虑对数据进行细分,使性能行为仍然是可见的(即每次调用.mean() 23 us ),但是总运行时间可能在1-2分钟以下,甚至更少。这将帮助您评估几种方法,然后再将它们完全应用到您的程序中。为了测试一些小的优化,一遍又一遍地运行整个过程是没有用的。

票数 0
EN

Software Engineering用户

发布于 2016-07-16 07:18:45

你已经忘记了一个最基本的问题:

用例的速度满意吗?

  • 如果答案是“是”,->就不要分析
  • 如果不是,你可以看看你的桌子。

但是老实说,它看起来并不十分有用,因为几乎所有的时间都花在OBSparser.py:48(解析)上,这需要很长的时间。我建议您将该方法重构为几个单独的方法。您可以使用可视化工具来可视化结果,py魅力对该用例有很好的支持。

票数 1
EN

Software Engineering用户

发布于 2016-07-16 07:19:32

这就是性能的非功能性需求

足够快的概念本身并没有什么技术意义。这取决于用户对您的产品的感知,并且应该通过需求进行翻译。这是判断实际实现是否足够快的唯一客观方法。

如果你没有这些要求,其他的都是猜测和非建设性的。

  • 用户告诉你,这个应用程序感觉很慢,但是在任何时候,任何人都会指定什么是毫秒,哪个硬件和哪个功能意味着什么?非建设性的:你不能在此基础上对代码进行改进,而且你根本无法判断在修订之前,代码的速度是不可接受的,现在,它已经足够快了。
  • 你认为一个特定的特性可以比现在运行得更快吗?这是不成熟的优化,不利于您的用户,他们可能根本不关心这个特性的速度,可能会优先考虑某个特定的bug,或者需要一个新的特性,或者需要其他更快的功能。

我如何知道我的代码是否可以改进?

假设它总是可以的。其中一些技术包括:

  • 重写代码以使用更多的内存,但使用更少的CPU,或者使用更多的CPU,但使用更少的内存。这常常导致代码很难阅读、理解和维护;这是应该避免过早优化的原因之一。
  • 使用不同的数据结构。
  • 依赖于缓存、预计算或使用OLAP多维数据集。
  • 移动低水平,包括下到汇编程序。
  • 没有完成任务。完全没有。这是从N秒到0的最终优化。
票数 1
EN
页面原文内容由Software Engineering提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://softwareengineering.stackexchange.com/questions/324987

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档