当调用成本很高时,通过Python中的单一职责原则(SRP)进行工作


12

一些基点:

  • 由于其解释性质,Python方法调用是“昂贵的”。从理论上讲,如果您的代码足够简单,那么分解Python代码除了会提高可读性和重用性之外,还会带来负面影响(这对开发人员而言是一大收获,对用户而言却不是很多)。
  • 单一责任原则(SRP)可使代码保持可读性,易于测试和维护。
  • 该项目具有特殊的背景,我们需要可读的代码,测试时间性能。

例如,像这样的调用多个方法(x4)的代码比随后的仅一个方法慢。

from operator import add

class Vector:
    def __init__(self,list_of_3):
        self.coordinates = list_of_3

    def move(self,movement):
        self.coordinates = list( map(add, self.coordinates, movement))
        return self.coordinates

    def revert(self):
        self.coordinates = self.coordinates[::-1]
        return self.coordinates

    def get_coordinates(self):
        return self.coordinates

## Operation with one vector
vec3 = Vector([1,2,3])
vec3.move([1,1,1])
vec3.revert()
vec3.get_coordinates()

与此相比:

from operator import add

def move_and_revert_and_return(vector,movement):
    return list( map(add, vector, movement) )[::-1]

move_and_revert_and_return([1,2,3],[1,1,1])

如果我要并行处理类似的事情,那么客观上我会失去性能。注意,这只是一个例子;我的项目有几个带有数学运算的微型例程-尽管使用起来更容易,但我们的分析器却不喜欢它。


在不影响Python性能的情况下,我们如何以及在什么地方采用SRP,因为它的固有实现会对其产生直接影响?

是否有解决方法,例如某种可以将内容在线发布的预处理器?

还是Python完全不擅长处理代码故障?


4
可能的重复进行编码时
t

19
就其价值而言,您的两个代码示例在职责数量上没有不同。 SRP不是计算锻炼的方法。
罗伯特·哈维

2
@RobertHarvey您是正确的,对不起这个糟糕的例子,我会在有空的时候编辑一个更好的例子。无论哪种情况,可读性和可维护性都会受到影响,并且随着我们减少类及其方法,SRP最终会在代码库中崩溃。
lucasgcb

4
请注意,尽管AOT编译器具有内联功能,但是函数调用在任何语言中都是昂贵的
Eevee

6
使用python的JITted实现,例如PyPy。应该主要解决此问题。
Bakuriu

Answers:


17

Python完全不擅长处理代码故障吗?

不幸的是,Python很慢,并且有很多轶事,人们通过内联函数并使代码变得丑陋而大大提高了性能。

有一个解决方法Cython,它是Python的编译版本,并且速度更快。

-编辑我只是想解决一些评论和其他答案。尽管它们的主旨可能不是特定于python的。但更普遍的优化。

  1. 在遇到问题然后寻找瓶颈之前不要进行优化

    一般来说很好的建议。但是假设是“正常”代码通常是高性能的。并非总是如此。每种语言和框架都有各自的特质。在这种情况下,函数调用。

  2. 只有几毫秒,其他东西会更慢

    如果您在功能强大的台式计算机上运行代码,那么只要您的单个用户代码在几秒钟内执行,就可能不在乎。

    但是业务代码倾向于为多个用户运行,并且需要一台以上的计算机来支持负载。如果您的代码运行速度快两倍,则意味着您可以拥有两倍的用户数量或一半的计算机数量。

    如果您拥有自己的机器和数据中心,那么通常您的CPU功耗就会很大。如果您的代码运行缓慢,则可以吸收它,至少直到需要购买第二台计算机为止。

    在如今的云计算中,您仅使用所需的计算能力,而不再使用,而对于性能不佳的代码,则存在直接成本。

    提高性能可以大幅度削减基于云的业务的主要费用,而性能确实应该放在首位。


1
尽管Robert's Answer有助于涵盖进行此类优化(可能适合该问题)背后的潜在误解的一些基础,但我认为这可以更直接,更符合Python上下文的方式回答这种情况。
lucasgcb

2
对不起,它有点短。我没有时间写更多。但是我确实认为罗伯特在这件事上是错的。与Python的最好的建议似乎是配置文件作为你的代码。不要以为它将是高性能的,只有在发现问题时才进行优化
Ewan

2
@Ewan:您不必先编写整个程序即可遵循我的建议。一两个方法足以获得足够的性能分析。
罗伯特·哈维

1
您也可以尝试pypy,这是一个JITted python
Eevee

2
@Ewan如果您真的担心函数调用的性能开销,那么您所做的任何事情可能都不适合python。但是,那时我真的想不出很多例子。绝大多数业务代码受IO限制,并且通常通过调用本机库(numpy,tensorflow等)来处理CPU繁重的工作。
Voo

50

实际上,许多潜在的性能问题并不是真正的问题。 您提出的问题可能就是其中之一。从语言上讲,我们称担心这些问题而没有证明它们是过早优化的实际问题

如果您正在编写Web服务的前端,则函数调用不会显着影响性能,因为通过网络发送数据的成本远远超过了进行方法调用的时间。

如果您正在编写一个紧密的循环以每秒刷新视频屏幕60次,则可能很重要。但是在那一点上,我声称如果您尝试使用Python来完成此任务,则可能会有更大的问题,而Python可能不适合该工作。

与往常一样,您发现的方法就是衡量。对代码运行性能分析器或某些计时器。看看在实践中这是否是一个真正的问题。


单一责任原则不是法律或授权;这是一个准则或原则。软件设计总是要权衡取舍。没有绝对的。牺牲可读性和/或可维护性来换取速度并不罕见,因此您可能必须在性能上牺牲SRP。但是,除非您知道自己存在性能问题,否则请不要进行权衡。


3
我认为这是真的,直到我们发明了云计算。现在,两个功能之一的成本实际上是另一个功能的4倍
Ewan

2
@Ewan 4次可能无关紧要,直到您测量出它足以引起关注的程度。如果Foo需要1毫秒,而Bar需要4毫秒,那就不好了。直到您意识到通过网络传输数据需要200毫秒。那时,Bar变慢并不重要。(仅一个可能的例子,即慢X倍不会产生明显或有影响的变化,并不意味着一定是超现实的。)
Becuzz,

8
@Ewan如果减少费用可以每月为您节省15美元,但每小时花费125美元的承包商要修复和测试它,则需要4个小时,因此我可以很容易地证明自己不值得花时间去做(或者至少做对了)现在,如果上市时间至关重要,等等。)总会有权衡取舍。在一种情况下有意义的事情在另一种情况下可能没有意义。
Becuzz

3
您的AWS账单确实非常低
Ewan

6
@Ewan AWS仍会分批舍入到上限(标准为100ms)。这意味着这种优化只会一劳永逸地避免将您推向下一个阶段,从而为您节省任何费用。
Delioth

2

首先,进行一些说明:Python是一种语言。有几种不同的解释器可以执行以Python语言编写的代码。引用实现(CPython)通常是当人们谈论“ Python”时就引用的实现,但在谈论性能特征时务必要精确,因为它们在实现之间可能会有很大差异。

在不影响Python性能的情况下,我们如何以及在什么地方采用SRP,因为它的固有实现会对其产生直接影响?

情况1。) 如果您有仅依赖于纯Python模块的纯Python代码(<= Python语言版本3.5、3.6具有“ beta级支持”),则可以在任何地方使用SRP并使用PyPy来运行它。PyPy(https://morepypy.blogspot.com/2019/03/pypy-v71-released-now-uses-utf-8.html)是一个Python解释器,具有即时编译器(JIT)并且可以删除功能只要有足够的时间通过跟踪执行的代码(几秒钟IIR​​C)来“热身”,就可以调用呼叫开销。**

如果您被限制使用CPython解释器,则可以将慢速函数提取到用C编写的扩展中,这些扩展将被预先编译并且不会受到任何解释器开销的困扰。您仍然可以在任何地方使用SRP,但是您的代码将在Python和C之间进行拆分。对于维护性而言,这是好还是坏比有选择地放弃SRP但仅坚持使用Python代码取决于您的团队,但是如果您有性能方面的关键部分,代码,它无疑比CPython解释的最优化的纯Python代码还要快。Python最快的许多数学库都使用此方法(numpy和scipy IIRC)。这是对案例2的一个很好的介绍...

情况2。) 如果您具有使用C扩展名的Python代码(或依赖于使用C扩展名的库),则PyPy可能有用也可能不会有用,这取决于它们的编写方式。有关详细信息,请参见http://doc.pypy.org/zh_CN/latest/extending.html,但摘要是CFFI的开销最小,而CTypes较慢(与PyPy一起使用可能比CPython慢​​)

Cython(https://cython.org/)是我没有太多经验的另一种选择。我出于完整性的考虑而提到它,因此我的回答可以“独立存在”,但不要求任何专业知识。从有限的使用量来看,感觉我必须更加努力才能获得与使用PyPy可以“免费”获得的相同的速度改进,并且如果我需要比PyPy更好的东西,编写我自己的C扩展也很容易(如果我在其他地方重用代码或将其中的一部分提取到库中,那么这是有好处的,我的所有代码仍然可以在任何Python解释器下运行,而Cython并不需要运行它)。

我害怕被“锁定”在Cython中,而任何为PyPy编写的代码也可以在CPython下运行。

**关于生产中PyPy的一些额外说明

在进行选择时要非常小心,这些选择具有在大型代码库中“锁定” PyPy的实际效果。由于某些(非常流行和有用的)第三方库由于前面提到的原因而表现不佳,因此如果您意识到需要这些库之一,可能会在以后导致非常困难的决策。我的经验主要是使用PyPy来加速某些(但不是全部)微服务,这些服务在公司环境中对性能敏感,这会给我们的生产环境增加微不足道的复杂性(我们已经部署了多种语言,其中有些语言使用的主要版本不同,例如2.7 vs 3.5仍在运行)。

我发现同时使用PyPy和CPython会迫使我编写仅依赖于语言规范本身的保证的代码,而不是依赖于随时更改的实现细节的代码。您可能会发现考虑这些细节会带来额外的负担,但是我发现它对我的专业发展很有价值,而且我认为对于整个Python生态系统来说,这是“健康的”。


是的 在这种情况下,我一直在考虑将重点放在C扩展上,而不是放弃原理并编写通配代码,其他答案给我的印象是,除非我从引用解释器中调换,否则它会很慢-要清除它,OOP仍然会在您看来是明智的做法?
lucasgcb

1
对于第一种情况(第二段),即使函数本身已被遵守,您也不会在调用函数时产生相同的负担吗?
伊万

CPython是唯一被普遍重视的解释器。PyPy很有趣,但是它肯定没有被广泛采用。此外,它的行为不同于 CPython,并且不适用于scipy等重要软件包。几乎没有理智的开发人员会推荐PyPy进行生产。因此,语言和实现之间的区别实际上是无关紧要的。
jpmc26

我想你虽然打在了头上。没有理由没有更好的解释器或编译器。python作为语言不是固有的。您只是被现实现实所困
-Ewan

@ jpmc26我已经在生产中使用了PyPy,建议向其他有经验的开发人员考虑使用PyPy。对于将falconframework.org用于轻量级REST API的微服务而言,它非常有用(作为示例)。行为不同是因为开发人员依赖于实现细节,而这些细节不能保证该语言并不是不使用PyPy的原因。这是重写代码的原因。如果CPython对其实现进行更改,则相同的代码可能仍然会中断(只要它仍然符合语言规范,就可以自由地执行此操作)。
史蒂文·杰克逊
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.