2012-11-02 40 views
2

我想节省时间并将对象标记为已修改,所以我编写了一个类并覆盖了它的__setattr__函数。重写__setattr__()的成本太高

import time 

class CacheObject(object): 
    __slots__ = ('modified', 'lastAccess') 
    def __init__(self): 
     object.__setattr__(self,'modified',False) 
     object.__setattr__(self,'lastAccess',time.time()) 

    def setModified(self): 
     object.__setattr__(self,'modified',True) 
     object.__setattr__(self,'lastAccess',time.time()) 

    def resetTime(self): 
     object.__setattr__(self,'lastAccess',time.time()) 

    def __setattr__(self,name,value): 
     if (not hasattr(self,name)) or object.__getattribute__(self,name)!=value: 
      object.__setattr__(self,name,value) 
      self.setModified() 

class example(CacheObject): 
    __slots__ = ('abc',) 
    def __init__(self,i): 
     self.abc = i 
     super(example,self).__init__() 

t = time.time() 
f = example(0) 
for i in range(100000): 
    f.abc = i 

print(time.time()-t) 

我测量了处理时间,花了2秒钟。当我评论了重写函数时,处理时间为0.1秒,我知道重载函数会更慢,但差距差不多是20倍。我想我必须弄错一些东西。

采取建议从CFI

1.elimate如果条件

def __setattr__(self,name,value): 
#  if (not hasattr(self,name)) or object.__getattribute__(self,name)!=value: 
      object.__setattr__(self,name,value) 
      self.setModified() 

运行时间降低到1.9,一点点改善,但标修改的对象,如果它不改变将花费更多的其他过程,所以不是一个选项。

2.change self.func到classname.func(个体经营)

def __setattr__(self,name,value): 
    if (not hasattr(self,name)) or object.__getattribute__(self,name)!=value: 
     object.__setattr__(self,name,value) 
     CacheObject.setModified(self) 

运行时间为2.0。所以没有什么改变

3)提取SetModified之功能

def __setattr__(self,name,value): 
    if (not hasattr(self,name)) or object.__getattribute__(self,name)!=value: 
     object.__setattr__(self,name,value) 
     object.__setattr__(self,'modified',True) 
     object.__setattr__(self,'lastAccess',time.time()) 

运行时间缩短到1.2 !!这很棒,它可以节省近50%的时间,但成本仍然很高。

+0

感谢您的号码反馈!我们可以争辩说涉及两个函数调用,并且由于删除一个将开销减少了50%,所以您可能会遇到剩余的开销。但也许别人有更多的想法。 – cfi

回答

1

不是一个完整的答案,但一些建议:

  1. 可以消除值进行比较?当然,这是你实现的功能改变。但是如果在属性中存储比整数更复杂的对象,运行时的开销将会变得更糟。

  2. 每个通过self调用方法都需要经过完整的方法解析顺序检查。我不知道Python是否可以执行任何MRO缓存本身。可能不是因为类型 - 动态原则。因此,您应该可以通过将self.method(args)更改为classname.method(self, args)来减少一些开销。这从通话中消除了MRO开销。这适用于您的settattr()实施中的self.setModified()。在大多数地方你已经完成了这个工作,参考了object

  3. 每一个函数调用都需要时间。你可以消除它们,例如将setModified的功能移动到__setattr__本身。

让我们知道这些中每一个的时序变化。我会分开实验。

编辑:感谢您的时间编号。

开销可能看起来很激烈(似乎仍然是10的因素)。然而,从整体运行时角度来看。换句话说:您在整个运行时间中将花费多少时间来设置这些跟踪的属性以及在其他地方花了多少时间?

在单线程应用程序Amdahl's Law is a simple rule中设置期望值。一个例子:如果1/3的时间花费在设置属性上,2/3做其他的东西。然后,将属性设置放慢10倍只会减慢30%。花在这些属性上的时间百分比越小,我们不得不关心的就越少。但是,如果你的百分比很高,这可能无法帮到你...

+0

这是我现在可以获得的最好的改进,我想我会转向下一步,谢谢。 – Max

0

覆盖__setattr__这里似乎没有任何功能。你只有两个属性,修改和lastAccess。这意味着这是你可以设置的唯一属性,那么为什么你会覆盖__setattr__?只需直接设置属性即可。

如果您希望在设置属性时发生某些情况,请使用setter和getter将其设为属性。这很容易,而且不那么神奇。

class CacheObject(object): 
    __slots__ = ('modified', 'lastAccess') 

    def __init__(self): 
     self.modified = False 
     self.lastAccess = time.time() 

    def setModified(self): 
     self.modified = True 
     self.lastAccess = time.time() 

    def resetTime(self): 
     self.lastAccess = time.time() 

class example(CacheObject): 
    __slots__ = ('_abc',) 
    def __init__(self,i): 
     self._abc = i 
     super(example,self).__init__() 

    @property 
    def abc(self): 
     self.resetTime() 
     return self._abc 


    @abc.setter 
    def abc(self, value): 
     self.setModified() 
     self._abc = value 
+0

CacheObject是一个超类,它的子类有各种变量来存储,它们都有相同的机制。 – Max

+0

@Max:然后你隐藏了从子类中发生的魔法。我会将每个属性改为一个属性,只需在这些属性中设置访问/修改。它是重复的,但不重复的。它不会咬你的脚。 –

+0

你的意思是使用@property和setter?我已经尝试过了,时间长达4.9s – Max

0

老问题,但值得更新。

我遇到了与使用python 3.6的pydantic相同的问题。

object.__setattr__(self, name, value)只是比正常设置类的属性慢。没有明显的方式。

如果性能很重要,唯一的选择是在需要覆盖_setattr_的类中保持对object.__setattr__(self, name, value)的绝对最小调用。