将作业分成两行仍然同样有效吗?

问题描述 投票:9回答:2

从python中的运行时效率角度来看,这些同样有效吗?

x = foo()
x = bar(x)

VS

x = bar(foo())

我有一个更复杂的问题,基本上可以归结为这个问题:显然,从代码长度的角度来看,第二个更有效,但运行时更好吗?如果他们不是,为什么不呢?

python performance optimization local-variables
2个回答
4
投票

这是一个比较:

第一种情况:

%%timeit
def foo():
    return "foo"

def bar(text):
    return text + "bar"

def test():
    x = foo()
    y = bar(x)
    return y

test()
#Output:
'foobar'
529 ns ± 114 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)

第二种情况:

%%timeit

def foo():
    return "foo"

def bar(text):
    return text + "bar"

def test():   
    x = bar(foo())
    return x

test()
#Output:
'foobar'
447 ns ± 34.6 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)

但这只是针对每种情况运行%% timeit的比较。以下是每种情况的20次迭代次数(以ns为单位):

df = pd.DataFrame({'First Case(time in ns)': [623,828,634,668,715,659,703,687,614,623,697,634,686,822,671,894,752,742,721,742], 
               'Second Case(time in ns)': [901,786,686,670,677,683,685,638,628,670,695,657,698,707,726,796,868,703,609,852]})

df.plot(kind='density', figsize=(8,8))

enter image description here

观察到,每次迭代,差异都在减小。该图显示性能差异不显着。从可读性的角度来看,第二种情况看起来更好。

在第一种情况下,将评估两个表达式:第一个表达式首先将foo()的返回值分配给x,然后第二个表达式将bar()分配给该值。这增加了一些开销。在第二种情况下,只评估一个表达式,一次调用两个函数并返回值。


2
投票

这很重要,但没有意义。 amanb's test只在其中一个测试中定义了函数的定义,因此必须在第一个测试中做更多的工作,从而扭曲结果。经过适当测试,结果的差别仅在于最薄的边距。使用相同的ipython %%timeit magic(IPython版本7.3.0,适用于Linux x86-64的CPython版本3.7.2),但从每个循环测试中删除函数的定义:

>>> def foo():
...     return "foo"
... def bar(text):
...     return text + "bar"
... def inline():
...     x = bar(foo())
...     return x
... def outofline():
...     x = foo()
...     x = bar(x)
...     return x
...

>>> %%timeit -r5 test = inline
... test()
...
...
332 ns ± 1.01 ns per loop (mean ± std. dev. of 5 runs, 1000000 loops each)


>>> %%timeit -r5 test = outofline
... test()
...
...
341 ns ± 5.62 ns per loop (mean ± std. dev. of 5 runs, 1000000 loops each)

inline代码更快,但差异低于10 ns / 3%。进一步内联(使身体只是return bar(foo()))节省了一点点,但再次,它是毫无意义的。

这也是你所期望的;存储和加载函数本地名称是CPython解释器可以做的最便宜的事情,函数is that outofline requires an extra STORE_FAST and LOAD_FAST(一个跟随另一个)之间的唯一区别,这些指令在内部实现为assignment toreading from编译时确定的插槽一个C数组,加上一个整数增量来调整引用计数。您为每个字节代码所需的CPython解释器开销付费,但实际工作的成本是微不足道的。

重点是:不要担心速度,编写更易读/可维护的代码版本。在这种情况下,所有名称都是垃圾,但如果foo的输出可以给出一个有用的名称,那么传递给bar,其输出被赋予一个不同的有用名称,并且没有这些名称,foobar之间的关系是非显而易见,不要内联。如果这种关系是显而易见的,并且foo的输出不会从命名中获益,那么就可以内联它。从局部变量中避免存储和负载是最微观的微观优化;它几乎不会在任何场景中导致有意义的性能损失,因此不要在其上建立代码设计决策。

© www.soinside.com 2019 - 2024. All rights reserved.