称泥 發表於 2021-4-25 22:51:00

python代码性能分析

<p>上一篇文章我们介绍了基准测试,通过基准测试可以发现程序变慢了,那么是因为什么原因导致性能变慢的,需要进一步做代码性能分析。python同样提供了性能分析工具。</p>
<h2 id="cprofile">cProfile</h2>
<p>cProfile是python默认的性能分析器,他只测量CPU时间,并不关心内存消耗和其他与内存相关联的信息。</p>
<pre><code class="language-py">from time import sleep
import random


def random_list(start, end, length):
    """
    生成随机列表
    :param start: 随机开始数
    :param end: 随机结束数
    :param length: 列表长度
    """
    data_list = []
    for i in range(length):
      data_list.append(random.randint(start, end))
    return data_list


def bubble_sort(arr):
    """
    冒泡排序: 对列表进行排序
    :param arr 列表
    """
    n = len(arr)
    sleep(1)
    for i in range(n):
      for j in range(0, n - i - 1):
            if arr &gt; arr:
                arr, arr = arr, arr
    return arr


if __name__ == '__main__':
    get_data_list = random_list(1, 99, 10)

    import cProfile
    cProfile.run('bubble_sort({})'.format(get_data_list))
</code></pre>
<p>继续使用上一篇文章中的例子,引用<code>cProfile</code>模块,<code>run()</code>方法参数说明。</p>
<p><code>run(statement, filename=None, sort=-1)</code></p>
<ul>
<li>statement: 需要测试的代码或者函数(函数名)</li>
<li>fielname: 结果保存的位置, 默认为stdout</li>
<li>sort: 结果排序方法,常用的有<code>cumtime</code>: 累积时间, <code>name</code>: 函数名, <code>line</code>: 行号</li>
</ul>
<p>为了使结果统计出耗时部分,我们加了<code>sleep</code>,结果如下:</p>
<pre><code class="language-shell">❯ python demo.py
         6 function calls in 1.004 seconds

   Ordered by: standard name

   ncallstottimepercallcumtimepercall filename:lineno(function)
      1    0.000    0.000    1.004    1.004 &lt;string&gt;:1(&lt;module&gt;)
      1    0.000    0.000    1.004    1.004 demo.py:19(bubble_sort)
      1    0.000    0.000    1.004    1.004 {built-in method builtins.exec}
      1    0.000    0.000    0.000    0.000 {built-in method builtins.len}
      1    1.004    1.004    1.004    1.004 {built-in method time.sleep}
      1    0.000    0.000    0.000    0.000 {method 'disable' of '_lsprof.Profiler' objects}
</code></pre>
<ul>
<li>
<p><code>6 function calls in 1.004 seconds</code>6个函数调用被监控,耗时1.004秒。</p>
</li>
<li>
<p><code>ncalls</code> 函数被调用的次数。如果这一列有两个值,就表示有递归调用,第二个值是原生调用次数,第一个值是总调用次数。</p>
</li>
<li>
<p><code>tottime</code> 函数内部消耗的总时间。(可以帮助优化)</p>
</li>
<li>
<p><code>percall</code> 是tottime除以ncalls,一个函数每次调用平均消耗时间。</p>
</li>
<li>
<p><code>cumtime</code> 之前所有子函数消费时间的累计和。</p>
</li>
<li>
<p><code>filename:lineno(function)</code> 被分析函数所在文件名、行号、函数名。</p>
</li>
</ul>
<h2 id="line_profiler">line_profiler</h2>
<p><code>line_profiler</code> 可以提供有关时间是如何在各行之间分配的信息,直白一点就是给出程序每行的耗时,在无法确定哪行语句最浪费时间,这很有用。</p>
<p>line_profiler是一个第三方模块,需要安装。</p>
<p>https://github.com/pyutils/line_profiler</p>
<pre><code class="language-py">from time import sleep
import random


def random_list(start, end, length):
    """
    生成随机列表
    :param start: 随机开始数
    :param end: 随机结束数
    :param length: 列表长度
    """
    data_list = []
    for i in range(length):
      data_list.append(random.randint(start, end))
    return data_list


@profile
def bubble_sort(arr):
    """
    冒泡排序: 对列表进行排序
    :param arr 列表
    """
    n = len(arr)
    sleep(1)
    for i in range(n):
      for j in range(0, n - i - 1):
            if arr &gt; arr:
                arr, arr = arr, arr
    return arr


if __name__ == '__main__':
    get_data_list = random_list(1, 99, 10)
    bubble_sort(get_data_list)
</code></pre>
<p>给需要监控的函数加上<code>@profile</code> 装饰器。通过<code>kernprof</code>命令运行文件(安装完line_profiler生成的命令)。</p>
<p>参数说明:</p>
<ul>
<li>
<p><code>-l</code>:以使用函数line_profiler</p>
</li>
<li>
<p><code>-v</code>:以立即将结果打印到屏幕</p>
</li>
</ul>
<p>运行结果:</p>
<pre><code class="language-shell">kernprof -l -v demo.py
Wrote profile results to demo.py.lprof
Timer unit: 1e-06 s

Total time: 1.00416 s
File: demo.py
Function: bubble_sort at line 18

Line #      Hits         TimePer Hit   % TimeLine Contents
==============================================================
    18                                           @profile
    19                                           def bubble_sort(arr):
    20                                             """
    21                                             冒泡排序: 对列表进行排序
    22                                             :param arr 列表
    23                                             """
    24         1          8.0      8.0      0.0      n = len(arr)
    25         1    1004030.0 1004030.0    100.0      sleep(1)
    26      11         15.0      1.4      0.0      for i in range(n):
    27      55         44.0      0.8      0.0          for j in range(0, n - i - 1):
    28      45         41.0      0.9      0.0            if arr &gt; arr:
    29      20         21.0      1.1      0.0                  arr, arr = arr, arr
    30         1          1.0      1.0      0.0      return arr
</code></pre>
<p>输出非常直观,分成了6列。</p>
<ul>
<li><code>Line #</code>:运行的代码行号。</li>
<li><code>Hits</code>:代码行运行的次数。</li>
<li><code>Time</code>:代码行的执行时间,单位为微秒。</li>
<li><code>Per Hit</code>:Time/Hits。</li>
<li><code>% Time</code>:代码行总执行时间所占的百分比。</li>
<li><code>Line Contents</code>:代码行的内容。</li>
</ul>
<p>只需查看<code>% Time</code>列,就可清楚地知道时间都花在了什么地方。</p>
<h2 id="总结">总结</h2>
<p>性能测试分析站在项目层面是一个很庞大的话题,以前为测试工程师,关注的是性能工具的使用,以及用户维度的性能;作为开发工程师,每个功能都是由一个个<code>函数/方法</code>组成,我们去分析每个<code>函数/方法</code>,甚至是每行<code>代码</code>的耗时,才能更好的进行代码层面的性能优化。</p><br><br>
来源:https://www.cnblogs.com/fnng/p/14702535.html
頁: [1]
查看完整版本: python代码性能分析