起因

前段时间,在做文本处理的实验时,需要预加载大量的原始数据(100W),在Python中使用的字典(dict)类型负责保存这些数据,很快就开发完成 了一个Demo版,然而程序执行的效率不是那么令人满意,通过使用Python中的profile发现,影响程序执行性能的关键语句就那么几条(用 dict保存加载后原始数据,这是个循环遍历。)

解决问题

既然找到了问题源,于是就勒起衣袖开始动手尝试使用各种解决方案替代效率不佳的,经过多次的反复尝试,调优的效果不太明显,最后一个idea:考虑Python垃圾回收机制的影响了,最后也证明了这个想法的靠谱程度,本文后续部分将分享调优的过程与测试结果。

浅谈Python垃圾回收机制

在使用C语言开发时代,我们的开发效率(生产力的问题)受牵制于内存释放、泄露等问题,于是普遍的口号---“指针好难学”。时过境迁,当今的流行的语言都配套了内存自动回收机制,从而使我们有更多的精力去纠结于业务上处理。
常用的垃圾回收(GC)算法有这几种引用计数(Reference Count)、Mark-Sweep、Copying、分代收集。在Python中使用的是前者引用计数,工作原理:为每个内存对象维护一个引用计数。因 此得知每次内存对象的创建与销毁都必须修改引用计数,从而在大量的对象创建时,需要大量的执行修改引用计数操作(footprint),对于程序执行过程 中,额外的性能开销是令人可怕的,由于该算法的特性问题,因此无法避免了,那么我们只能成垃圾回收时机着手了。
谈到这里因此不得不谈谈垃圾回收的时机,根据官方的描叙,Python中,有2中方式将会触发垃圾回收:
1、用户显示调用gc.collect()
2、每次Python为新对象分配内存时,检查threshold阀值,当对象数量超过threshold设置的阀值就开始进行垃圾回收。

调优之前

为了便于描叙如前文所述的加载大量原始数据的问题,使用了虚假的数据,这部分程序片段完成的功能倒没有发生变化,以下是调优之前的程序片段:

Python代码  
  1. data = range(1,5000000)
  2. wdict = dict(zip(data,data))

使用time python test.py运行,我们可以看到以下结果(机器的差异,显示的结果也将不同):

Python代码  
  1. real    0m39.066s
  2. user    0m26.422s
  3. sys     0m12.313s

如果使用linux下的top命令,您将动态的看到内存的使用情况,该程序运行后将缓慢的吞噬内存,由于程序运行时的内存对象都是有效的,因此垃圾回收触 发时,无法释放这部分内存,而垃圾回收程序却在做无用功(达到threshold阀值),显而易见,程序的执行性能将大打折扣。那么我们的调优手段也就很 容易了,在程序片段运行的这段时间内禁止进行垃圾回收。

调优之后

以下是调优之后的程序片段(禁止程序片段的垃圾回收):

Python代码  
  1. import gc
  2. gc.disable()
  3. data = range(1,5000000)
  4. wdict = dict(zip(data,data))
  5. gc.enable()

使用time python test.py运行,我们可以看到以下结果(机器的差异,显示的结果也将不同):

Python代码  
  1. real    0m2.760s
  2. user    0m1.208s
  3. sys     0m1.532s

通过上面的运行结果对比,显而易见,调优后的程序性能明显大幅提升,如果使用Linux下的top命令,您将动态的看到内存的使用情况也是不同的,该程序运行后迅速的吞噬内存,对比调优之前的程序片段,减少了垃圾回收的频频触发。

总结

通过这次的调优体验,发现Python垃圾回收频频触发将会影响程序执行的性能,因此,正如很多同学所说,Python程序的执行速度慢是不是有垃圾回收机制的一份功劳呢?

例外豆瓣算法工程师阿稳的一个类似的情况:

这两天在调试一个耗时大户的数据载入模块,因为时间的耗费居然主要都花在数据载入、解析、格式化上了,虽然处理的是很大量的数据,但还是心有不甘,不解决它使得后面的算法调试工作很难快捷深入的进行下去。

通过profile检查之后,把调试的目标锁定为文本解析的那一行代码上。因为数据的导出虽有几秒的耗时,但导出后数据的解析才是大头,只是这么一句简单的语句:

[row.split(',') for row in records]

很显然地,我会毫不犹豫地把罪魁祸首指定在split的身上。于是试图采用其它的方法来取代它,无论map还是直接取切片的方式都试过,就差没有用C来重写split函数了。但收效甚微。我甚至考虑把数据源切换到mysql,以避免做数据split这一步,但mysql的大数据量导出速度让我无法忍受。

无意中我用time.time()来计算了一下时间,很令人郁闷又很令人惊喜地发现,包含该行解析语句的子程序会越跑越慢。每次处理100W行的记录,第一次7秒,第二次14秒。如果从整个程序来启动这个模块,更加无法让人接受:7、15、50+、100+、200+。如果你的子程序运行得一次比一次慢,你会感觉生活很没有盼头。但我似乎找到真正的原因了:慢不是因为它本来就慢,而是在做同样的事情,却变得越来越慢。

于是想起以前Davies给我推荐过的一篇文章,讲python的垃圾回收(Garbage Collection)机制对程序性能的影响。当时由于主要并不是那个原因,所以没有太多研究。但这次的现象我可以原原本本地对上号,很有可能是因为数据量太大,处理过程中留下太多暂时不能清除的变量,而python的垃圾回收却在一遍一遍地扫这些不断在增长的列表,导致程序受到的影响越来越大。赶紧证实一下,import gc,然后在数据载入模块前gc.disable(),结束后再gc.enable()。结果原来要跑将近两个小时的程序,这下不用5分钟就跑完了。cool~!用gc.get_count()也证明了之前的猜想,在第一次运行之后临时变量数目就从几百上升到百万,并一直在涨。

由此我想,是不是有很多python新手对python速度的责难,都是因为这个原因呢(其实我觉得python基本模块的速度还是很快的:-))。另外,如果你的python程序在处理大数据量的问题,并且出现某个子程序在做同样量的工作,却越跑越慢的情况,恭喜你,你也许可以在这里对号入座,找到答案了。

Python垃圾回收(gc)拖累了程序执行性能?相关推荐

  1. python垃圾回收 (GC) 机制

    Python 能够自动进行内存分配和释放,但了解 python 垃圾回收 (garbage collection, GC) 的工作原理可以帮助你写出更好更快的 Python 程序.Python 使用两 ...

  2. Python垃圾回收和GC模块

    Python垃圾回收和GC模块 Python如何处理内存管理?了解Python垃圾回收系统的来龙去脉,以及如何避免它的陷阱. Python 为用户提供了许多便利,其中最大的便利之一就是(几乎)无障碍的 ...

  3. 【转】深入浅出图解C#堆与栈 C# Heap(ing) VS Stack(ing) 第六节 理解垃圾回收GC,提搞程序性能****

    前言 虽然在.Net Framework 中我们不必考虑内在管理和垃圾回收(GC),但是为了优化应用程序性能我们始终需要了解内存管理和垃圾回收(GC).另外,了解内存管理可以帮助我们理解在每一个程序中 ...

  4. python进阶19垃圾回收GC

    原创博客链接:python进阶19垃圾回收GC 垃圾收集三大手段 一.引用计数(计数器) Python垃圾回收主要以引用计数为主,分代回收为辅.引用计数法的原理是每个对象维护一个ob_ref,用来记录 ...

  5. python gc教程_python中的垃圾回收(GC)机制

    一.引用计数 Python 垃圾回收以引用计数为主,分代回收为辅.引用计数法的原理是每个对象维护一个ob_refcnt,用来记录对象被引用的次数,也就是用来追踪有多少个引用指向了对象,当发生以下四种情 ...

  6. python垃圾回收离职_垃圾回收gc.md

    垃圾回收gc python的垃圾收回机制不想c和c++是开发者自己管理维护内存的,python的垃圾回收是系统自己处理的,所以作为普通的开发者,我们不需要关注垃圾回收部分的内容,如果想要深层次理解py ...

  7. 垃圾回收GC经典算法

    目录 垃圾回收GC(Garbage Collection) 1.什么是垃圾 2.为什么要有GC 经典的GC算法 1.基本的一些概念 2.标记清除算法(Mark and Sweep) 3.复制法(cop ...

  8. 简述Python垃圾回收机制

    引言 许多高级语言都具有自己的垃圾回收机制,以管理计算机内存,Python也不例外.对于垃圾回收机制的了解程度,成了开发人员是否真正了解Python的检验手段,在面试的时候许多面试官也喜欢以此作为题目 ...

  9. python多线程详解 Python 垃圾回收机制

    文章目录 python多线程详解 一.线程介绍 什么是线程 为什么要使用多线程 总结起来,使用多线程编程具有如下几个优点: 二.线程实现 自定义线程 守护线程 主线程等待子线程结束 多线程共享全局变量 ...

最新文章

  1. AWS回应Elastic修改开源协议:创建“真正”开源的Elasticsearch分支
  2. 7月新的开始 - Axure学习03 - 布尔运算、表单元件
  3. 嵌入式系统开发方向的面试题总结
  4. 云计算概念诠释:18个权威人士的定义
  5. 【数据结构与算法】之深入解析“随机翻转矩阵”的求解思路与算法示例
  6. vs2005新建项目中没有ASP.NET WEB应用程序
  7. Magicodes.IE之快速导出Excel
  8. cad卸载_怎么把CAD卸载干净,老司机来教你
  9. 飞鸽传书2008一种重要心态
  10. 【报告分享】2019年中国数字经济发展指数.pdf(附下载链接)
  11. Percona 成为 MariaDB 基金会铜牌赞助商
  12. 基于python的性能负载测试Locust-6-脚本编写之使用HTTP client
  13. 杂谈(23)也说高考
  14. 资源 | 忘了Python关键语句?这份备忘录拯救你的记忆
  15. 007 定位明文封包call
  16. 全网最新正则表达式总结- 简单 便捷 (适用于新手学习正则表达式,老手巩固学习正则表达式)
  17. 论坛E-R图的思路及绘图,软件程序开发流程
  18. java 快递100_使用快递100 查询链接实现快速查询的示例
  19. 林轩田《机器学习基石》作业一-Python实现
  20. 一文教你看懂Fama-French三因子模型

热门文章

  1. VCLZip 简单 Demo
  2. 【转贴】ListView控件学习系列2-编辑ListView
  3. java留言板功能齐全源码_各类Java微信开发框架源码对比(建议收藏)
  4. pythonfor循环语句例子_Python for循环学习总结
  5. 算法学习(二)快速排序(下)
  6. 用jdbc连接hive server需要用到的 几个jar包
  7. 《『若水新闻』客户端开发教程》——10.代码编写(2)
  8. 使用C#开发ActiveX控件
  9. C#(WPF)为Grid添加实线边框。
  10. Vue.2.0.5-Class 与 Style 绑定