我 相 信 这 么 优秀 的 你
已 经 置 顶 了 我
翻译|王晗,谈海宇 选文|小象
转载请联系后台
一篇关于为了生产力牺牲性能的议论贴
我从Python的异步IO讨论中休息一下,谈论我最近思考的一些事情:Python的速度。简单自我介绍下,我是一个Python迷,我积极地在任何可以使用Python的地方使用它。人们对Python最大的抱怨之一是它慢。有些人甚至拒绝尝试Python,因为它比X其他语言速度慢。这里我想说下,尽管Python运行慢,但是为什么你应该尝试Python。
◇◆◇◆◇
速度不再重要
以前的情况下,程序要花很长的时间运行。CPU昂贵,内存昂贵。程序运行时间是一个重要的指标。计算机是非常昂贵,运行计算机耗费的电力同样很昂贵。这些资源的优化是因为一个永恒的商业法则:优化最昂贵的资源。
过去,最昂贵的资源是计算机运行时间。这使得计算机科学的研究,侧重于不同算法的效率。然而,现在不再是这样了,因为现在硅很便宜。真的很便宜。运行时间不再是你最昂贵的资源。现在公司最昂贵的资源是员工的时间。换句话说,是你。完成一件事情远比让一件事情快速的完成更重要。事实上,这是非常重要的,我要把这句话再次放在这里引用一下(为了那些只是粗略浏览的人):完成一件事情远比让一件事情快速的完成更重要。
你可能会说,“我公司关心速度,我建立了一个Web应用程序,所有的响应要比x毫秒更快。”或者,“我们有客户注销,因为他们认为我们的程序太慢了。“我不想说,速度并不重要,我只是想说它不再是最重要的;它不是你最昂贵的资源。
速度!
◇◆◇◆◇
速度是唯一重要的事情
当你在编程的时候说速度,你通常意味着性能,即CPU周期。当你的CEO在编程的时候说速度,他意味着商业速度。最重要的指标是产品上市时间。本质上,不管你的产品/ web应用程序有多快,不管它是用什么语言写的,甚至不管花多少钱使它运行,在一天结束的时候,决定你公司生存或死亡的一件事就是产品上市时间。我不只是谈论启动时的想法,直到你赚钱要多久,更多的是 “从想法到客户手中” 的时间框架。企业生存的唯一方法,是创新速度比你的竞争对手快。如果你的竞争对手在你之前上市,那无论你想出了多少好主意也都是无用的。你必须是第一个产品上市,或者至少跟上。一旦你放慢脚步,你就完蛋了。
企业生存的唯一方法,是创新速度比你的竞争对手快。
◇◆◇◆◇
微服务案例
像Amazon、Google 和 Netflix这样的公司明白快速前行的重要性。他们创造了一个业务系统,他们可以快速前行和快速创新。微服务是他们问题的解决方案。本文无关你是否应该使用微服务,但至少要理解为什么 Amazon 和 Google 认为他们应该使用微服务
微服务本质上是缓慢的。一个微服务的概念是打破边界的网络调用。这意味着你正在用所谓的函数调用(几个CPU周期)并把它变成一个网络调用。在性能方面它表现很差,但是你并不能做什么。与CPU相比,网络调用速度真的很慢。但这些大公司还是选择用微服务。没有一种架构比我知道的微服务还要慢了。微服务最大的弊端是性能,但最大的优势是上市时间。通过围绕较小的项目和代码库建立团队,一个公司能够以更快的速度进行迭代和创新。这只是说明了非常大的公司也关心上市时间,而不仅仅是初创企业。
◇◆◇◆◇
CPU不是你的瓶颈
如果你写一个网络应用程序,比如一个web服务器,很有可能CPU时间不是你应用程序的瓶颈。当您的Web服务器处理请求时,它可能产生几个网络调用,如您的数据库,或者像Redis缓存服务器。虽然这些服务本身可能是快速的,但是网络调用是缓慢的。有一个很好的博客文章是关于某些操作的速度差异。在这篇文章中,作者将CPU周期时间转化为更容易理解的人类时间。如果一个CPU周期相当于1秒,那么从加利福尼亚到纽约的网络调用时间相当于4年。网络就是这么慢的。粗略的估计,假设在同一数据中心内的正常网络调用大约需要3ms,这相当于我们“人类时间尺度”的3个月。现在想象你的程序是非常密集的CPU,它需要100000个周期来响应一个单一的调用。这将相当于超过1天的时间。现在假设你使用的是慢5倍的语言,现在大约需要5天。好吧,和我们3个月的网络调用时间相比,这4天的差别并不是很重要。如果有人需要花至少3个月的时间等待一个包,我真的认为额外的4天对于他们来说将不那么重要。
这最终意味着,即使Python很慢,也没关系。语言的速度(或CPU时间)几乎从来都不是问题。谷歌实际上做了一个关于这个概念的研究,他们写了一篇论文,论述了设计高吞吐量系统。在结论中,他们说:
在高吞吐量的环境下使用解释性的语言,这看起来似乎自相矛盾,但是我们发现CPU时间几乎不会成为限制因素;语言的表达性意味着,大多数程序都很小,他们的大部分时间花费在I / O和本地代码运行上。此外,无论是在实验的语言层面上,还是探索如何在许多机器上分布式的计算方法上,解释执行的灵活性是很有用的。
或者,强调:
CPU时间很少是限制因素。
◇◆◇◆◇
如果CPU时间是一个问题怎么办?
你可能会说,“都是这样就太好了,但我们有过CPU是我们的瓶颈这样的问题,造成我们的Web应用程序变慢”或“X语言比Y语言需要更少的硬件在服务器上运行”。这一切可能是真的。Web服务器的奇妙之处在于你几乎可以无限地平衡它们。换句话说,投入更多的硬件。当然,Python可能是比其他语言更需要好的硬件,比如C语言。只是在您的CPU问题上先抛开硬件问题,因为硬件相比于你的时间来说是非常便宜的。如果你在一年内节省了几周的生产时间,那么这种收入将超过你付出硬件的成本。
◇◆◇◆◇
那么,Python更快吗?
这一节我谈论的主要是开发时间怎么成为了最重要的事情。所以问题仍然是:谈到开发时间,Python是否比X其他语言快?目前来说,我,谷歌,还有很多人,都可以告诉你Python是有多高效。它为你抽象了很多东西,帮助你专注于你真正想编码的东西,而不必陷入小麻烦中,比如你是应该使用矢量还是数组。但你可能不喜欢从别人的话中接受它,所以让我们看看更多的经验数据。
在大多数情况下,关于Python是否更有效率的争论实际上是动态语言脚本与静态类型语言之间的PK。我认为人们普遍认为静态类型的语言效率不高,但这里有一篇好文章解释了为什么。就Python而言,这里有个关于用不同语言编写字符串处理需要多长时间的研究的很好的总结。
用各种语言编写字符串处理应用程序需要多长时间。(Prechelt和Garret)
根据上面的研究,python解决问题的效率几乎是使用java语言的2倍多。其他的研究也同样证实了这一点。罗塞塔对编程语言的不同点做了相对深入的研究。在他的论文里,他将python和其他脚本语言/解释型语言做了比较并得出结论:python相对于其他函数型语言是趋向于最简洁的。(平均要快1.2到1.6倍)
在python语言的使用中普遍的趋势是代码行数越来越少。代码行数听上去是一个让人讨厌的衡量标准,但是根据多方面的研究,包括之前已经提及的表明每行代码的执行耗费时间在每种语言中是相同的。因此,限制代码的行数是提高效率的有效途径。甚至一个叫“codinghorror”的c#程序员也写了一篇为何python更有效率的文章。
我认为可以说python比其他语言更有效率得益于python语言的内在动力以及强大的库。这有一篇简单的文章介绍python和其他语言的不同,如果你不明白为什么python如此小巧高效,我建议你有机会学一下python并亲自试一下,这将是你的第一个python程序:
import __hello__
◇◆◇◆◇
但是速度真的有那么重要吗?
运行性能
以上的一大堆观点可能让你误认为性能的优化和响应的速度不重要,但事实很多次证明性能在程序运行中十分重要。举一个网站应用程序的例子,当你的终端设备需要花很长的时间才能得到网站的响应时,你就知道多么需要程序的速度,多么需要提高网站的性能。
在我们的例子里,有两点需要注意:
1. 我们注意到单点的性能很慢。
2. 我们认识到之所以性能变慢是因为原来认为“足够快”的性能点衰落。
我们不需要在程序的每个模块进行性能优化。所有模块只需要做到“足够快”就能满足整体程序的要求。你的用户只能注意到几秒钟的响应变化,但是他们不可能注意到35ms到25ms的改变。“足够好”是我们真正需要达到的目标。声明:我需要说明的是这只适用于某些程序,而如果像实时投标程序每一毫秒都是很重要的。但是这是一个例外,并不适用此法则。
为了找到如何优化终端系统的第一步就是分析代码找到性能瓶颈所在,总而言之,就如Gene Kim所说的:
☆除了瓶颈以外的性能优化都是错觉☆
如果你没有找到系统的瓶颈所在,你的性能优化很可能是浪费了时间而且没有解决实际的问题。知道你找到瓶颈所在才能真正的解决问题。如果你在处理问题之前能找到瓶颈所在,你解决问题就像打地鼠一样有的放矢。优化代码之前的评估和确定瓶颈的方法是有名的“参数优化”。
Donald Knuth经常归功于以下的引用,但是他声明这个口号也是从别人哪那里“偷”来的。
☆过早的最优化是万恶之源☆
在谈论到维护代码库时更多的是引用Donald Knuth所说的:
☆我们应该忘记那些不重要的性能优化点,也就是大概97%的效率优化:过早的优化是万恶之源。但是我们不应该放过优化那关键的3%的机会。☆
换句话说,大部分的时间你应该忽略代码的性能优化。它的大部分代码质量达到足够好就行了。在性能不是足够好的时候,我们只需要注重那3%的代码优化就足够了。如果你通过上述的那样把大部分的时间花在大部分的代码而只是提高了几纳秒的性能你有可能劳而无功。要在评估以后优化它们才是明智之举。
参数优化包括调用某个高效的方法,甚至使用特定的被普遍认为很快的数据结构。
计算机科学家认为如果一个方法或者算法的性能与另外一个有着相同的线性增长,那么它们就是等价的,哪怕它们原来的实际的性能相差2倍。因为计算机性能发展的如此迅速以至于算法在数据结构方面的优化相比之下并不是那么的重要。也就是说,如果你有两个复杂度都为O(log n) 的函数,但是其中一个比另外一个慢2倍左右都是没有关系的随着运算量的增加,他们会以同样的速率降低性能。这也就是为什么说提前优化性能是万恶之源。因为它不仅浪费我们的时间而且并不能在性能的优化上给我们提供任何帮助。
因为这个原因,所以你可以认为当代码或者指令的行数为n时所有语言的复杂度都是O(n)。同样的指令的执行效率都是一样的。因为这个渐近线的逼近,所以所有语言的效率并不重要,每种语言的效率都是相同的。在这个逻辑下你可能会说我要为提前性能优化选择一个对于性能来说“更快”的语言。你的选择很可能脱离了评估,或者说并没有理解程序运行的真正瓶颈在哪里。
◇◆◇◆◇
最优化的python
我最爱python的原因是因为它能每次都让你的代码更加优化。你可以找到python代码中的一个方法正是你的瓶颈所在。你以前遵循指示可能需要优化很多次,而在python代码中你可以确信哪里正是你的瓶颈所在。Python有调用c的能力,这就意味着你能够重写这个方法来提高性能。你可以每次都这样执行。这个过程允许你能够使用任何一种语言优化瓶颈的方法并汇编到c可以兼容的汇编。这就让你大部分时间都花在python的代码上,只在必要的时候才用较低级的语言来写代码。
这里有种语言叫Cython,它是python的超集。它几乎合并了python和c并且日益成为一种类型化语言。Python代码在cython中也是可行的,并且cython最终编译成c语言。通过cython你可以写一个模块或者方法然后逐渐的将其转换成c语言的形式。你可以将C语言和python语言混合成“鸭子类型”。在瓶颈的地方采用cython来获得优化的完美组合,而在其他地方又能展现python的优美。
方小晗
数据挖掘攻城狮,机器学习爱好者,上海海事大学研究生,喜欢思考各种创新应用,挖掘大数据的价值,希望认识更多志同道合的小伙伴儿。
谈海宇
目前就职于中国移动苏州研发中心,硕士就读于南京邮电大学,信息网络专业,硕士期间研究方向为大数据、信息网络技术。