# 前言

1. 写法符合规范（如：该空格的地方打上空格，该换行的地方换行，名命方式符合规范等等）
2. 简洁且可读性高（能十行代码实现并且让人容易看懂的绝不写十一行，对经常重复出现的代码段落进行封装）
3. 性能高（如：运行时间尽可能短，运行时所用内存尽可能少）

# 先来段代码

Julia 集合，由式 $f_c(z) = z ^2 + c$ 进行反复迭代到。

$z_0, f_c(z_0), f_c(f_c(z_0)), ...$

import time
import numpy as np
import imageio
import PIL
import matplotlib.pyplot as plt
import cv2 as cv

x1, x2, y1, y2 = -1.8, 1.8, -1.8, 1.8
c_real, c_imag = -0.62772, -0.42193

def calculate_z_serial_purepython(maxiter, zs, cs):
output = [0] * len(zs)
for i in range(len(zs)):
n = 0
z = zs[i]
c = cs[i]
while abs(z) < 2 and n < maxiter:
z = z * z + c
n += 1
output[i] = n
return output

def calc_pure_python(desired_width, max_itertions):
x_step = (float(x2 - x1)) / float(desired_width)
y_step = (float(y2 - y1)) / float(desired_width)
x, y = [], []
ycoord = y1
while ycoord < y2:
y.append(ycoord)
ycoord += y_step
xcoord = x1
while xcoord < x2:
x.append(xcoord)
xcoord += x_step
zs, cs = [], []
for ycoord in y:
for xcoord in x:
zs.append(complex(xcoord, ycoord))
cs.append(complex(c_real, c_imag))
print(f"Length of x: {len(x)}")
print(f"Total elements: {len(zs)}")
start_time = time.time()
output = calculate_z_serial_purepython(max_itertions, zs, cs)
end_time = time.time()
secs = end_time - start_time
print("calculate_z_serial_purepython took", secs, "seconds")

assert sum(output) == 33219980
# # show img
# output = np.array(output).reshape(desired_width, desired_width)
# plt.imshow(output, cmap='gray')
# plt.savefig("julia.png")

if __name__ == "__main__":
calc_pure_python(desired_width=1000, max_itertions=300)


Length of x: 1000
Total elements: 1000000
calculate_z_serial_purepython took 25.053941249847412 seconds


# 开始分析

## 直接打印运行时间

from functools import wraps
def timefn(fn):
@wraps(fn)
def measure_time(*args, **kwargs):
start_time = time.time()
result = fn(*args, **kwargs)
end_time = time.time()
print("@timefn:" + fn.__name__ + " took " + str(end_time - start_time), " seconds")
return result
return measure_time


@timefn
def calculate_z_serial_purepython(maxiter, zs, cs):
...


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


python -m timeit -n 5 -r 5 -s "import code" "code.calc_pure_python(desired_width=1000, max_itertions=300)"


5 loops, best of 5: 24.9 sec per loop


## UNIX tine 命令

time -p python code.py

/usr/bin/time -p python code.py


Length of x: 1000
Total elements: 1000000
@timefn:calculate_z_serial_purepython took 14.34933090209961  seconds
calculate_z_serial_purepython took 14.350624322891235 seconds
real 15.57
user 15.06
sys 0.40


/usr/bin/time --verbose python code.py


Length of x: 1000
Total elements: 1000000
@timefn:calculate_z_serial_purepython took 7.899603605270386  seconds
calculate_z_serial_purepython took 7.899857997894287 seconds
Command being timed: "python code.py"
User time (seconds): 8.33
System time (seconds): 0.08
Percent of CPU this job got: 98%
Elapsed (wall clock) time (h:mm:ss or m:ss): 0:08.54
Average shared text size (kbytes): 0
Average unshared data size (kbytes): 0
Average stack size (kbytes): 0
Average total size (kbytes): 0
Maximum resident set size (kbytes): 98996
Average resident set size (kbytes): 0
Major (requiring I/O) page faults: 0
Minor (reclaiming a frame) page faults: 25474
Voluntary context switches: 0
Involuntary context switches: 2534
Swaps: 0
File system inputs: 0
File system outputs: 0
Socket messages sent: 0
Signals delivered: 0
Page size (bytes): 4096
Exit status: 0


## cProfile 模块

cProfile 模块是标准库内建三个的分析工具之一，另外两个是 hotshot 和 profile。

python -m cProfile -s cumulative code.py


-s cumulative 表示对每个函数累计花费的时间进行排序

36222017 function calls in 30.381 seconds

Ordered by: cumulative time

ncalls  tottime  percall  cumtime  percall filename:lineno(function)
1    0.000    0.000   30.381   30.381 {built-in method builtins.exec}
1    0.064    0.064   30.381   30.381 code.py:1(<module>)
1    1.365    1.365   30.317   30.317 code.py:35(calc_pure_python)
1    0.000    0.000   28.599   28.599 code.py:13(measure_time)
1   19.942   19.942   28.598   28.598 code.py:22(calculate_z_serial_purepython)
34219980    8.655    0.000    8.655    0.000 {built-in method builtins.abs}
2002000    0.339    0.000    0.339    0.000 {method 'append' of 'list' objects}
1    0.012    0.012    0.012    0.012 {built-in method builtins.sum}
4    0.003    0.001    0.003    0.001 {built-in method builtins.print}
1    0.000    0.000    0.000    0.000 code.py:12(timefn)
1    0.000    0.000    0.000    0.000 functools.py:44(update_wrapper)
4    0.000    0.000    0.000    0.000 {built-in method time.time}
1    0.000    0.000    0.000    0.000 <frozen importlib._bootstrap>:989(_handle_fromlist)
4    0.000    0.000    0.000    0.000 {built-in method builtins.len}
7    0.000    0.000    0.000    0.000 {built-in method builtins.getattr}
1    0.000    0.000    0.000    0.000 {built-in method builtins.hasattr}
5    0.000    0.000    0.000    0.000 {built-in method builtins.setattr}
1    0.000    0.000    0.000    0.000 functools.py:74(wraps)
1    0.000    0.000    0.000    0.000 {method 'update' of 'dict' objects}
1    0.000    0.000    0.000    0.000 {method 'disable' of '_lsprof.Profiler' objects}


python -m cProfile -o profile.stats code.py


>>> import pstats
>>> p = pstats.Stats("profile.stats")
>>> p.sort_stats("cumulative")
<pstats.Stats object at 0x000002AA0A6A8908>
>>> p.print_stats()
Sat Apr 25 16:38:07 2020    profile.stats

36222017 function calls in 30.461 seconds

Ordered by: cumulative time

ncalls  tottime  percall  cumtime  percall filename:lineno(function)
1    0.000    0.000   30.461   30.461 {built-in method builtins.exec}
1    0.060    0.060   30.461   30.461 code.py:1(<module>)
1    1.509    1.509   30.400   30.400 code.py:35(calc_pure_python)
1    0.000    0.000   28.516   28.516 code.py:13(measure_time)
1   20.032   20.032   28.515   28.515 code.py:22(calculate_z_serial_purepython)
34219980    8.483    0.000    8.483    0.000 {built-in method builtins.abs}
2002000    0.360    0.000    0.360    0.000 {method 'append' of 'list' objects}
1    0.012    0.012    0.012    0.012 {built-in method builtins.sum}
4    0.004    0.001    0.004    0.001 {built-in method builtins.print}
1    0.000    0.000    0.000    0.000 code.py:12(timefn)
1    0.000    0.000    0.000    0.000 C:\Users\ITryagain\AppData\Local\conda\conda\envs\tensorflow-gpu\lib\functools.py:44(update_wrapper)
4    0.000    0.000    0.000    0.000 {built-in method time.time}
1    0.000    0.000    0.000    0.000 <frozen importlib._bootstrap>:989(_handle_fromlist)
7    0.000    0.000    0.000    0.000 {built-in method builtins.getattr}
1    0.000    0.000    0.000    0.000 {built-in method builtins.hasattr}
4    0.000    0.000    0.000    0.000 {built-in method builtins.len}
1    0.000    0.000    0.000    0.000 C:\Users\ITryagain\AppData\Local\conda\conda\envs\tensorflow-gpu\lib\functools.py:74(wraps)
1    0.000    0.000    0.000    0.000 {method 'update' of 'dict' objects}
5    0.000    0.000    0.000    0.000 {built-in method builtins.setattr}
1    0.000    0.000    0.000    0.000 {method 'disable' of '_lsprof.Profiler' objects}

<pstats.Stats object at 0x000002AA0A6A8908>


>>> p.print_callers()
Ordered by: cumulative time

Function                                                                                              was called by...
ncalls  tottime  cumtime
{built-in method builtins.exec}                                                                       <-
code.py:1(<module>)                                                                                   <-       1    0.060   30.461  {built-in method builtins.exec}
code.py:35(calc_pure_python)                                                                          <-       1    1.509   30.400  code.py:1(<module>)
code.py:13(measure_time)                                                                              <-       1    0.000   28.516  code.py:35(calc_pure_python)
code.py:22(calculate_z_serial_purepython)                                                             <-       1   20.032   28.515  code.py:13(measure_time)
{built-in method builtins.abs}                                                                        <- 34219980    8.483    8.483  code.py:22(calculate_z_serial_purepython)
{method 'append' of 'list' objects}                                                                   <- 2002000    0.360    0.360  code.py:35(calc_pure_python)
{built-in method builtins.sum}                                                                        <-       1    0.012    0.012  code.py:35(calc_pure_python)
{built-in method builtins.print}                                                                      <-       1    0.000    0.000  code.py:13(measure_time)
3    0.003    0.003  code.py:35(calc_pure_python)
code.py:12(timefn)                                                                                    <-       1    0.000    0.000  code.py:1(<module>)
C:\Users\ITryagain\AppData\Local\conda\conda\envs\tensorflow-gpu\lib\functools.py:44(update_wrapper)  <-       1    0.000    0.000  code.py:12(timefn)
{built-in method time.time}                                                                           <-       2    0.000    0.000  code.py:13(measure_time)
2    0.000    0.000  code.py:35(calc_pure_python)
<frozen importlib._bootstrap>:989(_handle_fromlist)                                                   <-       1    0.000    0.000  code.py:1(<module>)
{built-in method builtins.getattr}                                                                    <-       7    0.000    0.000  C:\Users\ITryagain\AppData\Local\conda\conda\envs\tensorflow-gpu\lib\functools.py:44(update_wrapper)
{built-in method builtins.hasattr}                                                                    <-       1    0.000    0.000  <frozen importlib._bootstrap>:989(_handle_fromlist)
{built-in method builtins.len}                                                                        <-       2    0.000    0.000  code.py:22(calculate_z_serial_purepython)
2    0.000    0.000  code.py:35(calc_pure_python)
C:\Users\ITryagain\AppData\Local\conda\conda\envs\tensorflow-gpu\lib\functools.py:74(wraps)           <-       1    0.000    0.000  code.py:12(timefn)
{method 'update' of 'dict' objects}                                                                   <-       1    0.000    0.000  C:\Users\ITryagain\AppData\Local\conda\conda\envs\tensorflow-gpu\lib\functools.py:44(update_wrapper)
{built-in method builtins.setattr}                                                                    <-       5    0.000    0.000  C:\Users\ITryagain\AppData\Local\conda\conda\envs\tensorflow-gpu\lib\functools.py:44(update_wrapper)
{method 'disable' of '_lsprof.Profiler' objects}                                                      <-

<pstats.Stats object at 0x000002AA0A6A8908>


>>> p.print_callees()
Ordered by: cumulative time

Function                                                                                              called...
ncalls  tottime  cumtime
{built-in method builtins.exec}                                                                       ->       1    0.060   30.461  code.py:1(<module>)
code.py:1(<module>)                                                                                   ->       1    0.000    0.000  <frozen importlib._bootstrap>:989(_handle_fromlist)
1    0.000    0.000  code.py:12(timefn)
1    1.509   30.400  code.py:35(calc_pure_python)
code.py:35(calc_pure_python)                                                                          ->       1    0.000   28.516  code.py:13(measure_time)
2    0.000    0.000  {built-in method builtins.len}
3    0.003    0.003  {built-in method builtins.print}
1    0.012    0.012  {built-in method builtins.sum}
2    0.000    0.000  {built-in method time.time}
2002000    0.360    0.360  {method 'append' of 'list' objects}
code.py:13(measure_time)                                                                              ->       1   20.032   28.515  code.py:22(calculate_z_serial_purepython)
1    0.000    0.000  {built-in method builtins.print}
2    0.000    0.000  {built-in method time.time}
code.py:22(calculate_z_serial_purepython)                                                             -> 34219980    8.483    8.483  {built-in method builtins.abs}
2    0.000    0.000  {built-in method builtins.len}
{built-in method builtins.abs}                                                                        ->
{method 'append' of 'list' objects}                                                                   ->
{built-in method builtins.sum}                                                                        ->
{built-in method builtins.print}                                                                      ->
code.py:12(timefn)                                                                                    ->       1    0.000    0.000  C:\Users\ITryagain\AppData\Local\conda\conda\envs\tensorflow-gpu\lib\functools.py:44(update_wrapper)
1    0.000    0.000  C:\Users\ITryagain\AppData\Local\conda\conda\envs\tensorflow-gpu\lib\functools.py:74(wraps)
C:\Users\ITryagain\AppData\Local\conda\conda\envs\tensorflow-gpu\lib\functools.py:44(update_wrapper)  ->       7    0.000    0.000  {built-in method builtins.getattr}
5    0.000    0.000  {built-in method builtins.setattr}
1    0.000    0.000  {method 'update' of 'dict' objects}
{built-in method time.time}                                                                           ->
<frozen importlib._bootstrap>:989(_handle_fromlist)                                                   ->       1    0.000    0.000  {built-in method builtins.hasattr}
{built-in method builtins.getattr}                                                                    ->
{built-in method builtins.hasattr}                                                                    ->
{built-in method builtins.len}                                                                        ->
C:\Users\ITryagain\AppData\Local\conda\conda\envs\tensorflow-gpu\lib\functools.py:74(wraps)           ->
{method 'update' of 'dict' objects}                                                                   ->
{built-in method builtins.setattr}                                                                    ->
{method 'disable' of '_lsprof.Profiler' objects}                                                      ->

<pstats.Stats object at 0x000002AA0A6A8908>


## line_profiler 逐行分析

pip install line_profiler

conda install line_profiler


kernprof -l -v code.py


Wrote profile results to code.py.lprof
Timer unit: 1e-07 s

Total time: 137.019 s
File: code.py
Function: calculate_z_serial_purepython at line 23

Line #      Hits         Time  Per Hit   % Time  Line Contents
==============================================================
23                                           @profile
24                                           def calculate_z_serial_purepython(maxiter, zs, cs):
25         1      89776.0  89776.0      0.0      output = [0] * len(zs)
26   1000001    9990393.0     10.0      0.7      for i in range(len(zs)):
27   1000000    9244029.0      9.2      0.7          n = 0
28   1000000   10851654.0     10.9      0.8          z = zs[i]
29   1000000   10242762.0     10.2      0.7          c = cs[i]
30  34219980  558122806.0     16.3     40.7          while abs(z) < 2 and n < maxiter:
31  33219980  403539388.0     12.1     29.5              z = z * z + c
32  33219980  356918574.0     10.7     26.0              n += 1
33   1000000   11186107.0     11.2      0.8          output[i] = n
34         1         12.0     12.0      0.0      return output


In [1]: z = 0 + 0j

In [2]: %timeit abs(z) < 2
357 ns ± 21.1 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)

In [3]: n = 1

In [4]: maxiter = 300

In [5]: %timeit n < maxiter
119 ns ± 6.91 ns per loop (mean ± std. dev. of 7 runs, 10000000 loops each)


Total time: 132.816 s
File: code.py
Function: calculate_z_serial_purepython at line 23

Line #      Hits         Time  Per Hit   % Time  Line Contents
==============================================================
23                                           @profile
24                                           def calculate_z_serial_purepython(maxiter, zs, cs):
25         1      83002.0  83002.0      0.0      output = [0] * len(zs)
26   1000001    9833163.0      9.8      0.7      for i in range(len(zs)):
27   1000000    9241272.0      9.2      0.7          n = 0
28   1000000   10667576.0     10.7      0.8          z = zs[i]
29   1000000   10091308.0     10.1      0.8          c = cs[i]
30  34219980  531157092.0     15.5     40.0          while n < maxiter and abs(z) < 2:
31  33219980  393275303.0     11.8     29.6              z = z * z + c
32  33219980  352964180.0     10.6     26.6              n += 1
33   1000000   10851379.0     10.9      0.8          output[i] = n
34         1         11.0     11.0      0.0      return output


# 参考

1. 《Python 高性能编程》
posted @ 2020-04-25 18:17  ITryagain  阅读(612)  评论(6编辑  收藏
Live2D