我应该像C ++一样优化我的python代码吗?有关系吗?

时间:2009-08-30 11:49:35

标签: python performance

我和一位同事讨论过有效编写python的问题。他声称虽然你正在编写python,但你仍然必须尽可能地优化软件的一点点,就像你在C ++中编写一个有效的算法一样。

类似的事情:

  • if语句中or始终将条件最有可能首先失败,因此不会检查第二个。
  • 使用最有效的函数来操作常用的字符串。不是研磨字符串的代码,而是简单的事情,比如进行连接和拆分,以及查找子字符串。
  • 尽可能少地调用函数,即使它是以牺牲可读性为代价的,因为它会产生开销。

我说,在大多数情况下,这并不重要。我还应该说代码的背景是超高效的NOC或导弹制导系统。我们主要是在python中编写测试。

您对此事有何看法?

10 个答案:

答案 0 :(得分:14)

我对此的回答是:

  

我们应该忘记小事   效率,约占97%   时间:过早优化是   万恶之源。

(引用Knuth,Donald。结构化编程,参见陈述,ACM期刊计算调查,第6卷,第4期,1974年12月。第26页)


如果你的应用程序正在对数据库进行查询,那么一个查询将花费更多的时间,而不是那些你可以通过这些小优化获得的任何东西...

如果在这样的表演之后跑步,为什么不用汇编语言编码,毕竟?因为Python更容易/更快地编写和维护?好吧,如果是这样,你是对的: - )

最重要的是您的代码易于维护;不是几秒微秒的CPU时间!
好吧,也许除非您有数千台服务器 - 但这是您的情况吗?

答案 1 :(得分:13)

答案很简单:

  • 遵循Python最佳实践,而不是C ++最佳实践。
  • Python中的可读性比速度更重要。
  • 如果性能成为问题,请进行衡量,然后开始优化。

答案 2 :(得分:10)

这种过早的微优化通常在我的经验中浪费时间,即使在C和C ++中也是如此。首先编写可读代码。如果运行速度太慢,请通过分析器运行,如有必要,修复热点。

从根本上说,你需要考虑投资回报率。是否值得花费额外的精力来阅读和维护“优化​​”代码,节省几微秒?在大多数情况下,它不是。

(此外,编译器和运行时变得越来越聪明。随着时间的推移,一些微观优化可能会变成微观悲观。)

答案 3 :(得分:4)

我同意其他人:首先是可读代码(“在性能出现问题之前性能不是问题。”)。

我只想补充一点,当你绝对需要编写一些不可读和/或非直观的代码时,你通常可以用几个特定的​​方法来隔离它,为此可以编写详细的注释,并保留其余的代码高度可读。如果你这样做,你最终将拥有易于维护的代码,并且你只需要在真正需要的时候通过不可读的部分。

答案 4 :(得分:3)

  

我还应该说,代码的背景不是超高效的NOC或导弹制导系统。我们主要是在python中编写测试。

考虑到这一点,我会说你应该听取你的同事关于编写高效Python的建议,但忽略他所说的反对优先考虑代码的可读性和可维护性的任何东西,这可能比它的速度更重要。 ll执行。

答案 5 :(得分:2)

  

在带有或始终的if语句中   把条件最有可能失败   首先,所以第二个不会   检查。

这通常是一个很好的建议,也取决于你的程序的逻辑。如果第一个返回false,则第二个语句没有被评估是有意义的,那么这样做。反之亦然可能是一个错误。

  

使用最有效的功能   操纵常用的字符串。   不是研磨字符串的代码,而是   简单的事情,如做连接和   分裂,并找到子串。

我真的不明白这一点。当然你应该使用库提供的函数,因为它们可能用C实现,而纯python实现最有可能更慢。无论如何,不​​需要重新发明轮子。

  

尽可能少地调用函数,   即使它是以牺牲为代价的   可读性,因为开销   这创造了。

$ cat withcall.py
def square(a):
        return a*a

for i in xrange(1,100000):
        i_square = square(i)

$ cat withoutcall.py
for i in xrange(1,100000):
        i_square = i*i

$ time python2.3 withcall.py
real    0m5.769s
user    0m4.304s
sys     0m0.215s
$ time python2.3 withcall.py
real    0m5.884s
user    0m4.315s
sys     0m0.206s

$ time python2.3 withoutcall.py
real    0m5.806s
user    0m4.172s
sys     0m0.209s
$ time python2.3 withoutcall.py
real    0m5.613s
user    0m4.171s
sys     0m0.216s

我的意思是......来吧......拜托。

答案 6 :(得分:2)

我认为这里有几个相关的“城市传说”。

  • 错误在条件和类似的优化中首先放置经常检查的条件,为典型程序节省足够的时间,这对于典型的程序员来说是值得的。

  • True 有些人(但并不是很多人)在Python中使用此类样式的方式不正确。

  • True 许多人在认为它提高了Python程序的可读性时会在Python中使用这种样式。

关于可读性:我认为当你首先给出最有用的条件时它确实很有用,因为这是人们首先注意到的。你也应该使用''.join(),如果你的意思是连接字符串,因为它是最直接的方法(s += x操作可以意味着不同的东西)。

“尽可能少地调用函数”会降低可读性并违背Pythonic代码重用原则。因此,这不是人们在Python中使用的风格。

答案 7 :(得分:2)

在以可读性为代价引入性能优化之前,请研究像psyco这样的模块,它们会对不同的函数进行一些JIT-ish编译,通常会产生惊人的结果,而且不会损害可读性。

然后,如果您真的想要开始优化路径,首先必须学会测量和分析。优化必须是定量的 - 不要与你的直觉相关。热点分析器将向您显示程序燃烧最多时间的功能。

如果优化出现,经常会调用这样的函数:

def get_order_qty(ordernumber):
    # look up order in database and return quantity

如果有任何重复的ordernumbers,那么memoization将是一个很好的学习优化技术,它很容易打包在@memoize装饰器中,因此对程序的可读性几乎没有影响。 memoizing的效果是为一组给定的输入参数返回的值被缓存,因此昂贵的函数只能被调用一次,而后续的调用将针对缓存进行解析。

最后,考虑从循环中提升不变量。对于大型多维结构,这可以节省大量时间 - 实际上在这种情况下,我认为这种优化提高了可读性,因为它通常用于表明可以计算某些表达式在嵌套逻辑中的高级维度。

(顺便说一下,这真的是你的意思吗? •在if语句中,或者始终将条件最有可能首先失败,因此不会检查第二个条件。

我认为这可能是“和”的情况,但如果第一个值为True,则“或”会短路,从而保存条件第二项的评估。所以我会将此优化“规则”更改为:

  • 如果测试“A和B”,先将A放入 它更有可能评估为 假。
  • 如果测试“A或B”,请先将A放入 它更有可能评估 真。

但通常,条件的顺序是由测试本身驱动的:

if obj is not None and hasattr(obj,"name") and obj.name.startswith("X"):

你无法对这些进行重新排序以进行优化 - 它们按此顺序排列(或者只是让异常飞行并稍后捕获它们:

if obj.name.startswith("X"):

答案 8 :(得分:1)

当然遵循Python最佳实践(事实上我同意前两个建议),但可维护性和效率并不是对立的,它们主要是togethers(如果这是一个词)。

诸如“始终以某种方式为性能编写您的IF语句”之类的陈述是先验的,即不是基于您的程序花费时间的知识,因此是猜测。性能调优的第一个(或第二个,或第三个,无论如何)规则是不要猜测

如果在衡量,分析或在我的案例do this之后,您实际上知道您可以通过重新排序测试来节省大量时间。我的钱说的是1%或更低。

答案 9 :(得分:1)

我的内心反应如下:

我和你的同事一起工作,总的来说,我不会接受他们的建议。

问他是否曾经使用过探查器。