python的分段测试和优化

本文介绍了两种Python性能测试和优化的方法,包括利用timeit模块进行分段测试以及通过命令行调用cProfile模块进行详细分析。通过对代码的不同部分进行测试,可以有效地找出并优化性能瓶颈。
摘要由CSDN通过智能技术生成

/**

*  原文出处:

*  https://toucantoco.com/en/tech-blog/tech/python-performance-optimization

*  原文作者: Sylvain Josserand

*  原文发布: 2017-1-16

*/


第一个方案: 利用timeit 模块

#! /usr/bin/python

# -*- coding: utf-8 -*-


import random
import timeit


def wirte_sorted_letters(nb_letters=10**7):
    """
    首先创建一个长度为10的7次方的随机字符串;
    然后对字符串排序;
    然后写入文件。
    """
    random_string = ''
    for i in range(nb_letters):
        random_string += random.choice('abcdefghijklmnopqrstuvwxyz')

   sorted_string = sorted(random_string)

    with open("Sorted_text.txt", "w") as sorted_text:
        for character in sorted_string:
            sorted_text.write(character)

    return True


def main():

    sec1 = timeit.timeit(stmt=wirte_sorted_letters, number=1)
    print(sec1)

if __name__ == '__main__':
    main()

在我的机器上,timeit 计算出来的时间是14秒多点。



第二种方案:命令行调用cProfile模块。

然后去掉代码用引用 timeit 的部分。并且删除掉生成的文件。

在 cmd 中执行:

python -m cProfile -s tottime main.py

         62309408 function calls (62309365 primitive calls) in 20.256 seconds

   Ordered by: internal time

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
        1    6.599    6.599   20.231   20.231 main.py:8(wirte_sorted_letters)
 10000000    4.216    0.000    5.841    0.000 random.py:222(_randbelow)
 10000000    3.320    0.000    9.691    0.000 random.py:252(choice)
 10000000    2.189    0.000    2.189    0.000 {method 'write' of '_io.TextIOWrapper' objects}
        1    1.753    1.753    1.753    1.753 {built-in method builtins.sorted}
 12307695    1.180    0.000    1.180    0.000 {method 'getrandbits' of '_random.Random' objects}
 10000012    0.530    0.000    0.530    0.000 {built-in method builtins.len}
 10000000    0.445    0.000    0.445    0.000 {method 'bit_length' of 'int' objects}
        1    0.021    0.021   20.253   20.253 main.py:26(main)
       30    0.001    0.000    0.001    0.000 {built-in method nt.stat}
        1    0.001    0.001    0.001    0.001 {built-in method _imp.create_dynamic}
        1    0.001    0.001    0.001    0.001 {built-in method io.open}
       23    0.000    0.000    0.001    0.000 <frozen importlib._bootstrap_external>:1233(find_spec)
        3    0.000    0.000    0.000    0.000 {built-in method marshal.loads}
        5    0.000    0.000    0.000    0.000 {built-in method _imp.create_builtin}
        3    0.000    0.000    0.000    0.000 <frozen importlib._bootstrap_external>:830(get_data)
        8    0.000    0.000    0.000    0.000 {built-in method winreg.OpenKey}
        3    0.000    0.000    0.000    0.000 {method 'read' of '_io.FileIO' objects}
      112    0.000    0.000    0.000    0.000 <frozen importlib._bootstrap_external>:57(_path_join)
      112    0.000    0.000    0.000    0.000 <frozen importlib._bootstrap_external>:59(<listcomp>)
        1    0.000    0.000    0.003    0.003 random.py:38(<module>)
        4    0.000    0.000    0.002    0.000 <frozen importlib._bootstrap_external>:1117(_get_spec)
        9    0.000    0.000    0.002    0.000 <frozen importlib._bootstrap>:861(_find_spec)
      230    0.000    0.000    0.000    0.000 {method 'rstrip' of 'str' objects}



解读: 用cProfile模块 (-m cProfile)把全部的call分类,并按照 total time usage  排序( -s tottime) 。

           所以我们看到执行了1000万次random._randbelow 累计用时 5.8秒;

                                                1000万次random.choice,, 累计用时9.6秒;

                                                1000万次写文件,累计用时2.1秒;

                                                 对1000万长度的字符串排序,用时1.7秒。


第三种方案:程序中调用cProfile模块

修改main()成:

def main():
    cp = cProfile.Profile()
    cp.enable()

    wirte_sorted_letters()

    cp.disable()
    cp.print_stats()

并且删除以前生成的文件。然后运行:

         62306502 function calls in 20.279 seconds

   Ordered by: standard name

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
        1    0.000    0.000    0.000    0.000 _bootlocale.py:11(getpreferredencoding)
        1    6.683    6.683   20.279   20.279 main.py:9(wirte_sorted_letters)
 10000000    4.174    0.000    5.815    0.000 random.py:222(_randbelow)
 10000000    3.375    0.000    9.745    0.000 random.py:252(choice)
        1    0.000    0.000    0.000    0.000 {built-in method _locale._getdefaultlocale}
 10000000    0.555    0.000    0.555    0.000 {built-in method builtins.len}
        1    1.747    1.747    1.747    1.747 {built-in method builtins.sorted}
        1    0.000    0.000    0.000    0.000 {built-in method io.open}
 10000000    0.446    0.000    0.446    0.000 {method 'bit_length' of 'int' objects}
        1    0.000    0.000    0.000    0.000 {method 'disable' of '_lsprof.Profiler' objects}
 12306496    1.194    0.000    1.194    0.000 {method 'getrandbits' of '_random.Random' objects}
 10000000    2.103    0.000    2.103    0.000 {method 'write' of '_io.TextIOWrapper' objects}

时间结果没有什么不同,但是输出部分对应调用做了一些整理,更清楚了一些。


第四种方案: 使用line profile

pip install line_profiler

然后原代码段中函数的前面加一朵花

#! /usr/bin/python

# -*- coding: utf-8 -*-





import random





@profile

def wirte_sorted_letters(nb_letters=10**7):

    """

    首先创建一个长度为10的7次方的随机字符串;

    然后对字符串排序;

    然后写入文件。

    """

    random_string = ''

    for i in range(nb_letters):

        random_string += random.choice('abcdefghijklmnopqrstuvwxyz')

    sorted_string = sorted(random_string)



    with open("Sorted_text.txt", "w") as sorted_text:

        for character in sorted_string:

            sorted_text.write(character)



    return True





def main():



    wirte_sorted_letters()





if __name__ == '__main__':

    main()

root@Local:~# kernprof -l -v ~/codes/main.py
Wrote profile results to main.py.lprof
Timer unit: 1e-06 s

Total time: 18.0735 s
File: /root/codes/main.py
Function: wirte_sorted_letters at line 8

Line #      Hits         Time  Per Hit   % Time  Line Contents
==============================================================
     8                                           @profile
     9                                           def wirte_sorted_letters(nb_letters=10**7):
    10                                               """
    11                                               首先创建一个长度为10的7次方的随机字符串;
    12                                               然后对字符串排序;
    13                                               然后写入文件。
    14                                               """
    15         1            1      1.0      0.0      random_string = ''
    16  10000001      2747395      0.3     15.2      for i in range(nb_letters):
    17  10000000      7636799      0.8     42.3          random_string += random.choice('abcdefghijklmnopqrstuvwxyz')
    18         1      1351775 1351775.0      7.5      sorted_string = sorted(random_string)
    19                                           
    20         1          732    732.0      0.0      with open("Sorted_text.txt", "w") as sorted_text:
    21  10000001      2577197      0.3     14.3          for character in sorted_string:
    22  10000000      3759562      0.4     20.8              sorted_text.write(character)
    23                                           
    24         1            2      2.0      0.0      return True

还蛮有意思。。 我换到了Debian 虚拟机。资源少了,反而运行时间小了。



第五种方案, profiling

pip install profiling

好吧,移除掉函数前面的那朵花。



不是很看得懂。。。但是运行时间就长了很多。


做几个原作者推荐的优化:

1: 利用numpy 加快速度。

#! /usr/bin/python3
# -*- coding: utf-8 -*-


import numpy as np
import cProfile


def wirte_sorted_letters(nb_letters=10**7):
    """
    首先创建一个长度为10的7次方的随机字符串;
    然后对字符串排序;
    然后写入文件。
    """
    letters = tuple('abcdefghijklmnopqrstuvwxyz')

    random_letters = np.random.choice(letters, nb_letters)
    random_letters.sort()

    sorted_string = ''.join(i for i in random_letters)

    with open("Sorted_text.txt", "w") as sorted_text:
        for character in sorted_string:
            sorted_text.write(character)

    return True


def main():
    cp = cProfile.Profile()
    cp.enable()

    wirte_sorted_letters()

    cp.disable()
    cp.print_stats()

if __name__ == '__main__':
    main()

运行结果:

         20000012 function calls in 6.947 seconds

   Ordered by: standard name

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
        1    0.000    0.000    0.000    0.000 _bootlocale.py:11(getpreferredencoding)
        1    0.000    0.000    0.000    0.000 _methods.py:34(_prod)
        1    0.000    0.000    0.000    0.000 fromnumeric.py:2388(prod)
 10000001    2.342    0.000    2.342    0.000 main.py:20(<genexpr>)
        1    0.925    0.925    6.947    6.947 main.py:9(wirte_sorted_letters)
        1    0.000    0.000    0.000    0.000 {built-in method _locale._getdefaultlocale}
        1    0.000    0.000    0.000    0.000 {built-in method io.open}
        1    0.098    0.098    0.098    0.098 {method 'choice' of 'mtrand.RandomState' objects}
        1    0.000    0.000    0.000    0.000 {method 'disable' of '_lsprof.Profiler' objects}
        1    1.001    1.001    3.343    3.343 {method 'join' of 'str' objects}
        1    0.000    0.000    0.000    0.000 {method 'reduce' of 'numpy.ufunc' objects}
        1    0.461    0.461    0.461    0.461 {method 'sort' of 'numpy.ndarray' objects}
 10000000    2.120    0.000    2.120    0.000 {method 'write' of '_io.TextIOWrapper' objects}

对比一下方案三, 从20秒下降到6秒。。。。

然后把写文件部分改成

    with open("Sorted_text.txt", "w") as sorted_text:
        sorted_text.write(sorted_string)

         10000013 function calls in 3.918 seconds

   Ordered by: standard name

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
        1    0.000    0.000    0.000    0.000 _bootlocale.py:11(getpreferredencoding)
        1    0.000    0.000    0.000    0.000 _methods.py:34(_prod)
        1    0.000    0.000    0.000    0.000 fromnumeric.py:2388(prod)
 10000001    2.340    0.000    2.340    0.000 main.py:20(<genexpr>)
        1    0.003    0.003    3.918    3.918 main.py:9(wirte_sorted_letters)
        1    0.000    0.000    0.000    0.000 {built-in method _locale._getdefaultlocale}
        1    0.001    0.001    0.001    0.001 {built-in method io.open}
        1    0.095    0.095    0.096    0.096 {method 'choice' of 'mtrand.RandomState' objects}
        1    0.000    0.000    0.000    0.000 {method 'disable' of '_lsprof.Profiler' objects}
        1    0.997    0.997    3.337    3.337 {method 'join' of 'str' objects}
        1    0.000    0.000    0.000    0.000 {method 'reduce' of 'numpy.ufunc' objects}
        1    0.457    0.457    0.457    0.457 {method 'sort' of 'numpy.ndarray' objects}
        1    0.025    0.025    0.025    0.025 {method 'write' of '_io.TextIOWrapper' objects}

好吧,这个明显是坑嘛。


去掉cProfile, 最后程序是

#! /usr/bin/python3
# -*- coding: utf-8 -*-


import numpy as np


def write_sorted_letters(nb_letters=10**7):
    """
    首先创建一个长度为10的7次方的随机字符串;
    然后对字符串排序;
    然后写入文件。
    """
    letters = tuple('abcdefghijklmnopqrstuvwxyz')

    random_letters = np.random.choice(letters, nb_letters)
    random_letters.sort()

    sorted_string = ''.join(i for i in random_letters)

    with open("Sorted_text.txt", "w") as sorted_text:
        sorted_text.write(sorted_string)

    return True


def main():

    wirte_sorted_letters()

if __name__ == '__main__':
    main()



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值