- 内容提要
- 前言
- 作者简介
- 封面简介
- 第1章 理解高性能 Python
- 第2章 通过性能分析找到瓶颈
- 2.1 高效地分析性能
- 2.2 Julia 集合的介绍
- 2.3 计算完整的 Julia 集合
- 2.4 计时的简单方法——打印和修饰
- 2.5 用 UNIX 的 time 命令进行简单的计时
- 2.6 使用 cProfile 模块
- 2.7 用 runsnakerun 对 cProfile 的输出进行可视化
- 2.8 用 line_profiler 进行逐行分析
- 2.9 用 memory_profiler 诊断内存的用量
- 2.10 用 heapy 调查堆上的对象
- 2.11 用 dowser 实时画出变量的实例
- 2.12 用 dis 模块检查 CPython 字节码
- 2.13 在优化期间进行单元测试保持代码的正确性
- 2.14 确保性能分析成功的策略
- 2.15 小结
- 第3章 列表和元组
- 第4章 字典和集合
- 第5章 迭代器和生成器
- 第6章 矩阵和矢量计算
- 第7章 编译成 C
- 第8章 并发
- 第9章 multiprocessing 模块
- 第10章 集群和工作队列
- 第11章 使用更少的 RAM
- 第12章 现场教训
2.2 Julia 集合的介绍
让我们从Julia集合这个有趣的CPU密集型问题开始。这是一个可以产生复杂的输出图像的分形数列,以数学家Gaston Julia的名字命名。
函数的代码相当长,你不会想要自己实现一个。它包含一个CPU密集型的组件和一个显式的输入集合。这一配置允许我们分析CPU和RAM的使用情况以帮助我们了解代码中哪部分过多地消耗了这两项计算资源。将代码故意做成非最优的实现,这样我们就可以检查耗内存的操作和慢的语句。我们在本章后面将修正一个慢的语句和一个耗内存的语句,然后在第7章,我们还将显著提升整个函数的执行时间。
我们将分析一段能够生成一个伪灰阶图(图2-1)和一个纯灰阶变种(图2-3)的代码,设Julia集合的复数点c=-0.62772-0.42193j。一个Julia集合可以通过独立计算每一个像素点得到,也就是说这是一个“完美并行计算的问题”,每个点之间没有任何数据共享。
图2-1 Julia集合的伪灰阶图,细节高亮
如果我们选择一个不同的c,就会得到一个不同的图像。根据我们的选择,有些区域算起来较快而另一些会较慢,这有助于我们的分析。
问题的有趣之处在于我们对每一个像素的计算都需要进行一个次数不定的循环。每一次迭代都需要计算坐标值是趋于无穷,还是收敛。那些经过少数迭代就能算出结果的坐标在图2-1上为黑色,而那些需要大量迭代才能算出结果的坐标为白色。白色区域需要更多计算,因此生成时间更长。
让我们定义一个z的坐标函数进行计算。函数会计算复数z的平方加c:
我们迭代调用该函数并用abs计算逃逸条件。如果逃逸值为False,那么我们终止循环并在该坐标上记录下迭代的次数。如果逃逸条件始终不满足,那么我们在经过maxiter次迭代后终止,并将该z坐标转化为一个彩色像素点。
伪代码如下:
for z in coordinates: for iteration in range(maxiter): # limited iterations per point if abs(z) < 2.0: # has the escape condition been broken? z = z*z + c else: break # store the iteration count for each z and draw later
让我们试算两个坐标来解释这个函数。
首先,我们将使用图的左上角坐标-1.8-1.8j。在坐标更新前我们就必须首先计算逃逸条件abs(z) < 2:
z = -1.8-1.8j print abs(z) 2.54558441227
我们可以看到第0次迭代逃逸条件即为False,于是我们不需要更新坐标。该坐标的输出值就是0。
现在让我们跳到图中央的z = 0 + 0j并尝试几次迭代:
c = -0.62772-0.42193j z = 0+0j for n in range(9): z = z*z + c print "{}: z={:33}, abs(z)={:0.2f}, c={}".format(n, z, abs(z), c) 0: z= (-0.62772-0.42193j), abs(z)=0.76, c=(-0.62772-0.42193j) 1: z= (-0.4117125265+0.1077777992j), abs(z)=0.43, c=(-0.62772-0.42193j) 2: z=(-0.469828849523-0.510676940018j), abs(z)=0.69, c=(-0.62772-0.42193j) 3: z=(-0.667771789222+0.057931518414j), abs(z)=0.67, c=(-0.62772-0.42193j) 4: z=(-0.185156898345-0.499300067407j), abs(z)=0.53, c=(-0.62772-0.42193j) 5: z=(-0.842737480308-0.237032296351j), abs(z)=0.88, c=(-0.62772-0.42193j) 6: z=(0.026302151203-0.0224179996428j), abs(z)=0.03, c=(-0.62772-0.42193j) 7: z= (-0.62753076355-0.423109283233j), abs(z)=0.76, c=(-0.62772-0.42193j) 8: z=(-0.412946606356+0.109098183144j), abs(z)=0.43, c=(-0.62772-0.42193j)
我们可以看到,每次迭代都令abs(z) < 2为True。我们可以对该坐标迭代300次依然为True。我们无法得知需要多少次迭代才能令条件为False,可能是个无穷数列。最大迭代次数(maxiter)会确保我们不至于永远迭代下去。
我们在图2-2中可以看到前50个迭代结果。0+0j的结果数列(带圆形标记的实线)似乎每8个迭代出现一次循环,但是每个循环都跟前一个有微小的区别——我们无法得知该坐标是会永远迭代下去,还是将要迭代很长时间,还是马上就会超出边界条件。短划线cutoff表示+2的边界线。
图2-2 Julia集合的两个坐标例的演化结果
对于-0.82+0j的结果数列(带菱形标记的短划线),我们可以看到第9次迭代后绝对值结果就超出了+2的cutoff边界线,于是迭代终止。
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论