2012-11-02 57 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)的絕對最小調用。