《Python高性能编程》——2.2 Julia集合的介绍

2.2 Julia集合的介绍

让我们从Julia集合这个有趣的CPU密集型问题开始。这是一个可以产生复杂的输出图像的分形数列,以数学家Gaston Julia的名字命名。

函数的代码相当长,你不会想要自己实现一个。它包含一个CPU密集型的组件和一个显式的输入集合。这一配置允许我们分析CPU和RAM的使用情况以帮助我们了解代码中哪部分过多地消耗了这两项计算资源。将代码故意做成非最优的实现,这样我们就可以检查耗内存的操作和慢的语句。我们在本章后面将修正一个慢的语句和一个耗内存的语句,然后在第7章,我们还将显著提升整个函数的执行时间。

我们将分析一段能够生成一个伪灰阶图(图2-1)和一个纯灰阶变种(图2-3)的代码,设Julia集合的复数点c=-0.62772-0.42193j。一个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的边界线。

对于-0.82+0j的结果数列(带菱形标记的短划线),我们可以看到第9次迭代后绝对值结果就超出了+2的cutoff边界线,于是迭代终止。

时间: 2024-11-05 12:35:29

《Python高性能编程》——2.2 Julia集合的介绍的相关文章

《Python高性能编程》——导读

前 言 Python很容易学.你之所以阅读本书可能是因为你的代码现在能够正确运行,而你希望它能跑得更快.你可以很轻松地修改代码,反复地实现你的想法,你对这一点很满意.但能够轻松实现和代码跑得够快之间的取舍却是一个世人皆知且令人惋惜的现象.而这个问题其实是可以解决的. 有些人想要让顺序执行的过程跑得更快.有些人需要利用多核架构.集群,或者图形处理单元的优势来解决他们的问题.有些人需要可伸缩系统在保证可靠性的前提下酌情或根据资金多少处理更多或更少的工作.有些人意识到他们的编程技巧,通常是来自其他语言

《Python高性能编程》——2.3 计算完整的Julia集合

2.3 计算完整的Julia集合 我们在本节分解Julia集合的生成代码.我们将在本章以各种方法分析它.如例2-1所示,在模块的一开始,我们导入time模块作为我们的第一种分析手段并定义一些坐标常量. 例2-1 定义空间坐标的全局常量 """Julia set generator without optional PIL-based image drawing""" import time # area of complex space to i

《Python高性能编程》——第2章 通过性能分析找到瓶颈 2.1 高效地分析性能

第2章 通过性能分析找到瓶颈 读完本章之后你将能够回答下列问题 如何找到代码中速度和RAM的瓶颈? 如何分析CPU和内存使用情况? 我应该分析到什么深度? 如何分析一个长期运行的应用程序? 在CPython台面下发生了什么? 如何在调整性能的同时确保功能的正确? 性能分析帮助我们找到瓶颈,让我们在性能调优方面做到事半功倍.性能调优包括在速度上巨大的提升以及减少资源的占用,也就是说让你的代码能够跑得"足够快"以及"足够瘦".性能分析能够让你用最小的代价做出最实用的决定

《Python高性能编程》——1.2 将基本的元素组装到一起

1.2 将基本的元素组装到一起 仅理解计算机的基本组成部分并不足以理解高性能编程的问题.所有这些组件的互动与合作还会引入新的复杂度.本段将研究一些样本问题,描述理想的解决方案以及Python如何实现它们. 警告:本段可能看上去让人绝望--大多数问题似乎都证明Python并不适合解决性能问题.这不是真的,原因有两点.首先,在所有这些"高性能计算要素"中,我们忽视了一个至关重要的要素:开发者.原生Python在性能上欠缺的功能会被迅速开发出来.另外,我们会在本书中介绍各种模块和原理来帮助减

《Python高性能编程》——2.10 用heapy调查堆上的对象

2.10 用heapy调查堆上的对象 Guppy项目有一个内存堆的调查工具叫作heapy,可以让你查看Python堆中对象的数量以及每个对象的大小.当你需要知道某一时刻有多少对象被使用以及它们是否被垃圾收集时,你尤其需要这种深入解释器内部去了解内存中实际内容的能力.如果你受困于内存泄漏(可能由于你的对象的引用隐藏于一个复杂系统中),那么这个工具能帮你找到问题的关键点. 当你在审查你的代码,看它是否如你预期那样生成对象时,你会发现这个工具非常有用--结果很可能令你吃惊,并为你带来新的优化方向. 为

《Python高性能编程》——第1章 理解高性能Python 1.1 基本的计算机系统

第1章 理解高性能Python 读完本章之后你将能够回答下列问题 计算机架构有哪些元素? 常见的计算机架构有哪些? 计算机架构在Python中的抽象表达是什么? 实现高性能Python代码的障碍在哪里? 性能问题有哪些种类? 计算机编程可以被认为是以特定的方式进行数据的移动和转换来得到某种结果.然而这些操作有时间上的开销.因此,高性能编程可以被认为是通过降低开销(比如撰写更高效的代码)或改变操作方式(比如寻找一种更合适的算法)来让这些操作的代价最小化. 数据的移动发生在实际的硬件上,我们可以通过

《Python高性能编程》——1.3 为什么使用Python

1.3 为什么使用Python Python具有高度的表现力且容易上手--新开发者会很快发现他们可以在很短时间里做到很多.许多Python库包含了用其他语言编写的工具,使Python可以轻易调用其他系统.比如,scikit-learn机器学习系统包含了LIBLINEAR和LIBSVM(两者皆以C语言写成),numpy库则包含了BLAS以及其他用C和Fortran语言写的库.因此,正确运用这些库的Python代码确实可以在速度上做到跟C媲美. Python被誉为"内含电池",因为它内建了

《Python高性能编程》——2.6 使用cProfile模块

2.6 使用cProfile模块 cProfile是一个标准库内建的分析工具.它钩入CPython的虚拟机来测量其每一个函数运行所花费的时间.这一技术会引入一个巨大的开销,但你会获得更多的信息.有时这些额外的信息会给你的代码带来令人惊讶的发现. cProfile是标准库内建的三个分析工具之一,另外两个是hotshot和profile.hotshot还处于实验阶段,profile则是原始的纯Python分析器.cProfile具有跟profile一样的接口,且是默认的分析工具.如果你对这些库的历史

《Python高性能编程》——2.9 用memory_profiler诊断内存的用量

2.9 用memory_profiler诊断内存的用量 和Rober Kern实现的line_profiler包测量CPU占用率类似,Fabian Pedregosa和Philippe Gervais实现的memory_profiler模块能够逐行测量内存占用率.了解代码的内存使用情况允许你问自己两个问题: 我们能不能重写这个函数让它使用更少的RAM来工作得更有效率? 我们能不能使用更多RAM缓存来节省CPU周期? memory_profiler的操作和line_profiler十分类似,但是运