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

    xiaoxiao2024-06-21  109

    本节书摘来自异步社区《Python高性能编程》一书中的第2章,第2.10节,作者[美] 戈雷利克 (Micha Gorelick),胡世杰,徐旭彬 译,更多章节内容可以访问云栖社区“异步社区”公众号查看。

    2.10 用heapy调查堆上的对象

    Guppy项目有一个内存堆的调查工具叫作heapy,可以让你查看Python堆中对象的数量以及每个对象的大小。当你需要知道某一时刻有多少对象被使用以及它们是否被垃圾收集时,你尤其需要这种深入解释器内部去了解内存中实际内容的能力。如果你受困于内存泄漏(可能由于你的对象的引用隐藏于一个复杂系统中),那么这个工具能帮你找到问题的关键点。

    当你在审查你的代码,看它是否如你预期那样生成对象时,你会发现这个工具非常有用——结果很可能令你吃惊,并为你带来新的优化方向。

    为了使用heapy,你需要用命令pip install guppy安装guppy包。

    例2-12的代码是Julia代码的一个略有修改的版本。calc_pure_python使用了堆对象hpy,我们在三个地方打印堆的内容。

    例2-12 用heapy查看代码运行时对象数量的变化

    def calc_pure_python(draw_output, desired_width, max_iterations): ... while xcoord < x2: x.append(xcoord) xcoord += x_step from guppy import hpy; hp = hpy() print "heapy after creating y and x lists of floats" h = hp.heap() print h print zs = [] cs = [] for ycoord in y: for xcoord in x: zs.append(complex(xcoord, ycoord)) cs.append(complex(c_real, c_imag)) print "heapy after creating zs and cs using complex numbers" h = hp.heap() print h print print "Length of x:", len(x) print "Total elements:", len(zs) start_time = time.time() output = calculate_z_serial_purepython(max_iterations, zs, cs) end_time = time.time() secs = end_time - start_time print calculate_z_serial_purepython.func_name + " took", secs, "seconds" print print "heapy after calling calculate_z_serial_purepython" h = hp.heap() print h print

    例2-13的输出显示了内存的使用在创建zs和cs列表后变得有趣:它增长了大约80MB,因为2000000个复数对象消耗了64000000字节。这些复数对象占据了目前使用的大多数内存。如果你想要优化这个程序的内存用量,这个结果可用于揭示目前保存的对象数量以及它们总共占用的空间。

    例2-13 heapy输出显示了我们代码执行时每一个主要阶段的对象总数

    $ python julia1_guppy.py heapy after creating y and x lists of floats Partition of a set of 27293 objects. Total size = 3416032 bytes. Index Count % Size % Cumulative % Kind (class / dict of class) 0 10960 40 1050376 31 1050376 31 str 1 5768 21 465016 14 1515392 44 tuple 2 199 1 210856 6 1726248 51 dict of type 3 72 0 206784 6 1933032 57 dict of module 4 1592 6 203776 6 2136808 63 types.CodeType 5 313 1 201304 6 2338112 68 dict (no owner) 6 1557 6 186840 5 2524952 74 function 7 199 1 177008 5 2701960 79 type 8 124 0 135328 4 2837288 83 dict of class 9 1045 4 83600 2 2920888 86 __builtin__.wrapper_descriptor <91 more rows. Type e.g. '_.more' to view.> heapy after creating zs and cs using complex numbers Partition of a set of 2027301 objects. Total size = 83671256 bytes. Index Count % Size % Cumulative % Kind (class / dict of class) 0 2000000 99 6400000 76 64000000 76 complex 1 185 0 16295368 19 80295368 96 list 2 10962 1 1050504 1 81345872 97 str 3 5767 0 464952 1 81810824 98 tuple 4 199 0 210856 0 82021680 98 dict of type 5 72 0 206784 0 82228464 98 dict of module 6 1592 0 203776 0 82432240 99 types.CodeType 7 319 0 202984 0 82635224 99 dict (no owner) 8 1556 0 186720 0 82821944 99 function 9 199 0 177008 0 82998952 99 type <92 more rows. Type e.g. '_.more' to view.> Length of x: 1000 Total elements: 1000000 calculate_z_serial_purepython took 13.2436609268 seconds heapy after calling calculate_z_serial_purepython Partition of a set of 2127696 objects. Total size = 94207376 bytes. Index Count % Size % Cumulative % Kind (class / dict of class) 0 2000000 94 64000000 68 64000000 68 complex 1 186 0 24421904 26 88421904 94 list 2 100965 5 2423160 3 90845064 96 int 3 10962 1 1050504 1 91895568 98 str 4 5767 0 464952 0 92360520 98 tuple 5 199 0 210856 0 92571376 98 dict of type 6 72 0 206784 0 92778160 98 dict of module 7 1592 0 203776 0 92981936 99 types.CodeType 8 319 0 202984 0 93184920 99 dict (no owner) 9 1556 0 186720 0 93371640 99 function <92 more rows. Type e.g. '_.more' to view.>

    第三段显示了在计算完Julia集合后,我们占用了94MB的内存。除了之前的复数,我们现在还保存了大量的整数,列表中的项目也变多了。

    hpy.setrelheap()可以用来创建一个内存断点,当后续调用hpy.heap()时就会产生一个跟这个断点的差额。这样你就可以略过断点前由Python内部操作导致的内存分配。

    最新回复(0)