不得不服!Python速度虽然慢,但是它工作效率很高!
写在前面
让我们来讨论一个我最近一直在思考的问题:Python 的性能。顺便说一下,我是 Python 的忠实拥趸,我在各种情况下都会积极尝试使用 Python 来解决问题。大家对 Python 最大的抱怨就是它的速度慢。有些人甚至因为 Python 的速度不如某个语言而拒绝使用它。本文中我将阐述,即便 Python 这么慢,为什么还值得你对它进行尝试。
速度不再关键
之前,程序的运行时间相当长。CPU 资源和内存资源都十分珍贵,程序的运行时间在这种情况下是一个重要指标。计算机本身十分昂贵,当然还有随之而来昂贵的电力消耗。优化这些资源就十分必要,因为在商业世界有一个永恒的规则:
优化你最昂贵的资源。
历史上,程序最昂贵的资源是计算机的运行时间。这也就导致了对计算机科学的研究更专注于不同算法的效率。然而在当下环境中,这已经不再适用,现在硅的价格已经十分便宜了。是真的非常便宜。运行时间不再是你最昂贵的资源。一个公司最昂贵的资源现在是其雇佣的员工的时间。也就是正在看这篇文章的你自己的时间。对现在的公司来说,完成项目比让项目跑得更快更重要。这点非常重要,这里再次强调:
完成项目比让项目跑得更快更重要。
你也许会说“我们公司对性能要求很高,我构建的网站应用需要所有的请求在 X 毫秒内返回。”或者“客户认为我们的应用慢而放弃使用我们的应用。”在这里我不是说速度根本不重要,我只是想说明速度不再是最重要的指标,因为它不再是你最昂贵的资源。
速度!
速度是唯一重要的事情
在编程的世界中当你提到速度,一般是指程序的性能,也就是 CPU 周期。而当你的 CEO 提到速度,他通常指的是业务上的速度,其中最重要的是投入市场的时间。你的产品或网络应用有多快并不重要,应用采用哪种语言编写的也不重要,甚至是使项目运行投入了多少资金都不重要。最终,唯一能够让你的公司存活下来的是产品投入市场的时间。
这里不是指初创公司观念中的盈利时间,而更多是从想法转换到实际消费者手中所花费的时间。在商业世界中能存活下来的唯一方法是比你的竞争对手更快地进行创新。如果你的竞争对手比你更早地发布产品,那么你有再多的好点子也无济于事。你必须成为市场的第一个进入者,或至少要赶上领先的节奏。一旦你掉队了,那么你就大势已去。
在商业世界中能存活下来的唯一方法是比你竞争对手更快地进行创新。
微服务的例子
亚马逊、谷歌、Netflix 等公司深刻理解速度的重要性。它们创建了一个能快速发展和创新的业务系统。微服务就是这个问题的解决方案。本文并不讨论你是不是应该使用微服务,但最起码亚马逊和谷歌认为它们应采用微服务。
微服务天生就很慢。微服务的最基础的概念就是拆分业务边界,并通过网络调用来相互通讯。这也就意味着你需要把一个只占几个 cpu 周期的方法调用转换成网络调用。从性能层面上来说,这简直糟糕透顶。网络调用的速度和 CPU 调用根本不可同日而语。但是那些大公司仍然选择使用微服务。没有比微服务更慢的架构了。
微服务的最大劣势就是其性能,但是它所带来的最大好处是缩短了投入市场需要的时间。通过构建小型项目和少量代码的团队,公司可以以一个非常快的速度进行迭代与演进。这个例子只是为了展示不仅仅是初创公司,大公司也关注投入市场所需的时间。
CPU 并非你的瓶颈
如果你编写像网络服务器上的网络应用,那么 CPU 时间可能并非你应用的瓶颈。当你的网络服务器处理一个请求,它可能会需要调用多个网络调用,例如数据库或 Redis 缓存。这些服务本身速度很快,然而网络调用的过程却很慢。一篇博客很好地描述了各个特定操作速度上的差别。其中,作者将 CPU 时间对应到人们易于理解的时间。如果单个 CPU 周期对应一秒的话,一个从加利福尼亚到纽约的网络调用就大约相当于 4 年。
对,网络调用就是这么慢。粗略地估计,在同一数据中心内的一个普通的网络调用需要 3 毫秒,这在前面的对应关系下相当于 3 个月。现在假如你的程序是 CPU 密集型的,需要花费 100,000 个 CPU 周期来处理一次调用。按之前的比例来算,这些时间相当于 1 天。那么如果你用一个慢 5 倍的语言,它也就只花费了 5 天。相对于 3 个月的网络调用,4 天的差别就无足轻重了。如果用户在等待一个至少需要 3 个月的包裹,那么 4 天的差别相对来说就不那么重要了。
说了这么多我只是想说,即便 python 很慢,但这并不重要。语言的速度(也就是 CPU 时间)几乎不会导致问题。谷歌就这个概念做过一个研究,并写了一篇论文。论文中谈论了设计高吞吐量的系统。在结论中这样描述到:
在一个高吞吐量的环境中使用一个解释型语言看似矛盾,但是我们发现 CPU 时间几乎不是瓶颈因素,表达性强的语言意味着大部分代码是短小的,大多数时间花费在了 I/O 以及原生代码运行时上。此外,解释型的实现所具备的灵活性十分有用,它方便了我们在语言层面上的试验,也方便了我们探索将计算分布到多台机器上的方法。
简单说来:
CPU 时间几乎不是瓶颈因素。
那如果 CPU 时间的确是系统的瓶颈呢?
你可能会说“这观点很好,但是我们确实在 CPU 上遇到了瓶颈,造成了我们网络应用的速度缓慢”,或者“在服务器上 X 语言相对 Y 语言需要更少的硬件资源来运行。”这可能都是事实。但网络应用的优势就是你可以几乎无限地进行负载均衡。换而言之,就是使用更多的硬件资源。当然 Python 相较其他语言,如 C 语言,可能需要更多硬件资源。那就使用更多的硬件来解决这个问题。硬件相对于你的人工时间便宜许多。如果你一年内节约了几周的开发时间,这就远胜于你在硬件上所节约下来的花费。
那么,Python 到底快不快?
前面我谈论了最重要的是开发所花费的时间。但是问题还是没有得到回答:Python 的开发时间的确比其他语言快么?经过多方调查,我、谷歌以及许多第三方结论都会告诉你 Python 能提升多大产能。Python 抽象化了诸多内容,可以让你专注于你真正的业务逻辑,而不用关心你是应该使用 vector 还是 array 等底层细节问题。你可能不相信这道听途说的观点,所以让我们看一些经验数据。
总体来说,争论 python 是否高产,最终讨论的是脚本(或动态语言)与静态类型语言之间的比较。我认为大家都赞同静态类型语言的产量较低,但这里有一篇很好的论文解释了其中的原因。就 Python 而言,曾有研究分析了不同语言编写一个字符串处理程序所花费的时间,并做了很好的总结。
使用不同语言编写字符串处理应用所花费的时间。(Prechelt 与 Garret)
在结论中 Python 比 Java 的生产效率高两倍。还有其他诸多研究结果得到类似的结论。Rosetta Code 对不同语言进行了公平而深入地研究。在论文中它们将 Python 和其他脚本 / 解释型语言进行了比较,并认为:
Python 是其中最精练的,甚至比函数式语言更好(平均短 1.2-1.6 倍)。
总体看来 Python 代码的行数总是更少。代码行数听上去是一个糟糕的指标,但是多项研究显示(包括之前提及的两个),在各语言中输入每行代码的时间是不相上下的。因此,减少代码行数也就相当于提高了生产效率。就连 C# 程序员 codinghorror 也写了一篇文章阐述 Python 具有更高的产量。
我认为这已经足够能说明 Python 相较于诸多其他语言更高产。这主要归功于 Python 的开箱即用以及丰富的第三方包。这里有一篇文章简述了 Python 和其他语言的差别。如果你不知道为什么 Python 这么“小”还这么高产,我推荐你学习一下 Python 来亲自体验一下,下面将是你的第一行程序:
import __hello__
如果运行速度对你真的很重要?
上述观点的论调听上去像认为优化和速度根本不重要。但是事实是,许多时候运行时效率至关重要。一个例子是,你的网络应用有一个特定的端点需要相当长的时间来响应请求。同时你知道它需要有多快,也知道它要被优化到什么程度。
在这个例子中,发生了下面两件事:
- 我们关注到某个运行慢的端点。
- 我们认为它慢,因为我们了解什么是足够快,并且它没能达到这个指标。
我们不必在应用中对每个服务进行细节调优。每个服务只需要能“足够快”来满足用户的需求就够了。用户会发现某个端点花费了几秒时间返回,但是他们并不会注意到你把一个 35 毫秒的请求优化到了 25 毫秒。你只需要达到“足够好”就可以了。免责声明:不得不说一些应用,如实时拍卖应用,确实需要细节调优,能提升一毫秒算一毫秒。但是这是一个特例,而不是业界的规则。
为了弄清如何优化某个端点,第一步你需要对你的代码进行性能分析,并尝试整理出其中的瓶颈。归根到底:
任何不考虑瓶颈的调优都是幻想。—— Gene Kim
如果你的优化并不解决瓶颈,那你就是在浪费你的时间,而且还不能解决真正地问题。不解决瓶颈,你就不会在性能上得到显著的提升。如果你尝试着在了解瓶颈前优化,你就像在和你的代码在玩打地鼠的游戏。在排查和确定瓶颈前优化代码也是“不成熟优化”的表现。Donald Knuth 常被引用下面的观点,虽然他本人称这也是从其他人那儿听来的:
不成熟的优化是万恶之源。
Donald Knuth 在一次关于维护代码库的讨论中进行了下面的完整描述:
我们应该忘记那些小的性能提升,这占了 97% 的时间:不成熟的优化是万恶之源。但同时我们也不能放过那至关重要的 3%。
换句话来说,大部分时间,你不应该关心代码的优化。它们通常已经足够好了。如果没有能达到标准,我们应该只需要改变那 3% 的代码。你并不会因为你的代码使用一个 if 替代了一个方法,得到几毫秒的性能提升而获得任何奖励。只有在分析之后再进行优化。
不成熟的优化包含盲目调用某个更快的方法,或使用一个特定的数据结构只因为其总体上更快。计算机科学认为两个方法或算法有一样的渐进增长(或时间复杂度),那么就可以认为它们性能是相同的,就算其中之一比另一个慢两倍。计算机的速度太快,算法在计算上的增长,如数据或使用量的增长比算法本身重要得多。
换而言之,如果你有两个 O(log n) 的方法,一个是另一个速度的两倍,这之间的差别根本无关紧要。随着数据量的增长,它们都会以相同的速度变慢。这也就是为什么不成熟的优化是万恶之源,它会浪费我们的时间,最终却在提升性能上帮不上我们什么忙。
就时间复杂度而言,你可以认为用任何的语言写你的程序的复杂度都是 O(n) 的,其中 n 是代码的行数或指令个数。同一指令的增长速率都是相同的。所以一个语言或运行时的快慢并不重要,就渐进增长而言,所有语言都是等价的。在这个逻辑下,你可以认为,因为某个语言速度快而选择其为开发你应用的语言是不成熟优化的一种体现。你不应该主观地判断某个语言快而不去进行衡量、不去了解将会遇到的瓶颈。
因为某个语言速度快而选择其为开发你应用的语言是不成熟优化的一种体现。
优化 Python
我最喜欢 Python 的一点就是它可以让你一步一步地优化你的代码。比如说你有一个 Python 方法,你发现它是你项目中的瓶颈。你已经对其优化了数次,可能是遵循了这里或这里的意见,现在你确定 Python 本身是你应用的瓶颈所在。
Python 是能够直接调用 C 代码的,这就意味着你可以用 C 重写这个方法来减少性能问题。你可以一个一个地进行替换。这个过程能让你调用任何最终编译成 C 兼容指令的优化的代码,也让你能在大部分情况下继续使用 Python,而只在真正需要的时候深入底层进行开发。
有一个叫 Cython 的语言,它是 Python 的超集。几乎是 Python 和 C 的结合体,同时它是渐进的类型化语言。任何 Python 代码都是合法的 Cython 代码,Cython 会将代码编译成 C 代码。有了 Cython,你可以编写模块或方法,渐渐地引入 C 语言的类型和性能。你可以混合使用 C 语言的类型和 Python 的鸭子类型(duck type)。通过 Cython 你可以只在瓶颈处进行调优,而在其他地方仍然使用优美的 Python 语言,两者能完美地结合。
使用 Python 编写的太空大规模多人在线游戏 EVE Online 的截图
当你最终遇到了 Python 的性能瓶颈,你不需要将你所有代码移植到其他语言。你总是可以使用 Cython 重写部分方法来满足性能上的需求。这也是游戏 EVE Online 所采用的策略。Eve 是一个大型多人在线电脑游戏,它完全使用 Python 和 Cython 开发。游戏开发人员通过在 C/Cython 中调优瓶颈来达到游戏级的性能要求。如果游戏都能达到性能上的需求,那么大部分情况都应该可以满足。
此外,还有其他方法来优化你的 Python 程序。例如 PyPy 是一个 Python 的运行时编译执行(JIT)的实现,只需要使用 PyPy 切换默认的 Cython,就可以显著地提升你长时间运行应用的运行时性能,如在网络服务器上。
不得不服!Python速度虽然慢,但是它工作效率很高!的更多相关文章
- Python批量图片去水印,提高工作效率
平常工作中,有时为了采用网络的一些素材,但这些素材往往被打了水印,如果我们不懂PS就无法去掉水印,或者无法批量去掉水印.这些就很影响我们的工作效率. 今天我们就一起来,用Python + OpenC ...
- 给大家推荐:五个Python小项目,Github上的人气很高的
1.深度学习框架 Pytorch https://github.com/pytorch/pytorch PyTorch 是一个 Torch7 团队开源的 Python 优先的深度学习框架,提供两个高级 ...
- 为什么Python 3.6以后字典有序并且效率更高?
在Python 3.5(含)以前,字典是不能保证顺序的,键值对A先插入字典,键值对B后插入字典,但是当你打印字典的Keys列表时,你会发现B可能在A的前面. 但是从Python 3.6开始,字典是变成 ...
- python经典书记必读:Python编程快速上手 让繁琐工作自动化
所属网站分类: 资源下载 > python电子书 作者:熊猫烧香 链接:http://www.pythonheidong.com/blog/article/69/ 来源:python黑洞网,专注 ...
- 入门python:《Python编程快速上手让繁琐工作自动化》中英文PDF+代码
入门推荐学习<python编程快速上手>前6章是python的基础知识,通俗易懂地讲解基础,初学者容易犯错的地方,都会指出来.从第三章开始,每章都有一个实践项目,用来巩固前面所学的知识. ...
- 《Python编程快速上手 —让繁琐工作自动化》|百度网盘免费下载|Python自动化办公
Python编程快速上手—让繁琐工作自动化 提取码:u8vj 如今,人们面临的大多数任务都可以通过编写计算机软件来完成. Python 是一种解释型.面向对象.动态数据类型的高级程序设计语言.通过 P ...
- Python编程快速上手-让繁琐工作自动化-第二章习题及其答案
Python编程快速上手-让繁琐工作自动化-第二章习题及其答案 1.布尔数据类型的两个值是什么?如何拼写? 答:True和False,使用大写的T和大写的F,其他字母是小写. 2.3个布尔操作符是什么 ...
- 使用ant优化android项目编译速度,提高工作效率
1.Android项目编译周期长,编译项目命令取消困难 2.在进行Android项目的编译的同时,Eclipse锁定工作区不能进行修改操作 3.在只进行资源文件的修改时,Eclipse对资源文件的修改 ...
- 用Python写了一个postgresql函数,感觉很爽
用Python写了一个postgresql函数,感觉很爽 CREATE LANGUAGE plpythonu; postgresql函数 CREATE OR REPLACE FUNCTION myfu ...
随机推荐
- div可编辑框,去除粘贴文字样式😄
上个月做了个聊天的需求(网页版的).说到聊天都想到输入框,说到输入框都会想到input,但是input标签是不支持插入图片的(包括areatext标签).查阅了一些资料就看到div标签有一个属性con ...
- mobienet, shufflenet
参考github上各位大神的代码 mobilenet和shufflenet,实现起来感觉还是各种问题. mobilenet目前使用的代码来自这里:https://github.com/BVLC/caf ...
- JS高阶函数与函数柯里化
高阶函数 满足下列条件之一的函数: 函数作为参数被传递(如回调函数): 函数可以作为返回值输出: 一些内置高阶函数的例子: Array.prototype.map map()方法通过调用对输入数组中的 ...
- MySQL存储引擎与索引
引言: MySQL存储引擎主要分为 InnoDB 存储引擎与 MyISAM 存储引擎.都采用B+数的存储结构. 应用场景: InnoDB适合:(1)可靠性要求比较高,要求事务:(2)大量 insert ...
- ndk-build 学习笔记
# 必须以local_path 开头# 定位源文件LOCAL_PATH := $(call my-dir) #引入clear-vars.mk文件,清除除local_path以外的其他local_< ...
- FirstBird--项目流程
创建项目(英文路径)—–img图片文件 创建窗体–设置大小(Basic—size–>320*480)—最大化功能禁用(Expert–>setResizable(false)) 添加面板–设 ...
- SPOJ8222 NSUBSTR - Substrings(后缀自动机)
You are given a string S which consists of 250000 lowercase latin letters at most. We define F(x) as ...
- 在mac上显示网速的软件——iStat Menus 5:
在mac上显示网速的软件——iStat Menus 5: https://bjango.com/mac/istatmenus/ 注册码: Email: 982092332@qq.com SN: GAW ...
- Spring Boot多环境配置
在项目开发阶段,开发环境和实际生产环境是不一样,比如使用的数据库/服务连接配置等.因此,配置多个开发环境profile还是必要的 多环境的配置(yml)方式 配置其实很简单,在resource目录下, ...
- JVM——Java内存区域
一,概述: Java跟C++不同,在内存管理区域C++程序员拥有着最高权力,但是正是因为如此,所以C++程序员要照顾这个对象的生老病死,从创建到消亡都是由程序员决定的. 但是Java程序员在虚拟机的自 ...