2011-09-22 121 views
8

在閱讀Python re模塊上的文檔時,我決定看看re.py源代碼。Python重新模塊的緩存清除

當我打開一看,我發現這一點:

_cache = {} 
_MAXCACHE = 100 

def _compile(*key): 
    cachekey = (type(key[0]),) + key 
    p = _cache.get(cachekey) 
    if p is not None: 
     return p 

    #...Here I skip some part of irrelevant to the question code... 

    if len(_cache) >= _MAXCACHE: 
     _cache.clear() 
    _cache[cachekey] = p 
    return p 

爲什麼緩存使用_cache.clear(),當它到達條目_MAXCACHE清除?

清除緩存完全並從頭開始是常用的方法嗎?

爲什麼剛纔不用最長時間之前兌現的價值被刪除?

+3

有趣的問題。我認爲編寫這段代碼的開發者可能會懶惰,或者「簡單勝於複雜」的想法。 :-) – NPE

+0

我認爲可能有一些科學研究證明這種清除緩存的方法可以達到某個恆定值。 – ovgolovin

+0

查看正在開發的新正則表達式模塊的源代碼可能很有趣:http://bugs.python.org/issue2636。說明中包含術語「智能緩存」,因此可能會在該區域做出一些改進。 –

回答

3

如果我不得不猜測,我會說這樣做是爲了避免跟蹤單個值何時/多長時間存儲在緩存中,這會造成內存和處理開銷。因爲正在使用的緩存對象是一本固有無序的字典,所以沒有好方法知道在沒有其他緩存對象的情況下向其中添加了哪些訂單項目。這可以通過使用OrderedDict代替標準字典來解決,假設您使用的是Python> = 2.7,但是除此之外,您需要重新設計緩存的實現方式,以消除對clear()

+0

使用'OrderedDict'實現'cache'很難嗎?在我看來,使用元素的順序可以按照最後使用的順序排列它們。重新使用編譯對象只需要將緩存的值移動到「OrderedDict」的開頭,方法是將其彈出並重新放入字典中。 – ovgolovin

+0

@ovgolovin - 您可以彈出/重新添加該值以將其移回列表底部,這是可能的。我不會覺得這很難,不。 –

+0

我認爲可能有一些科學研究證明這種清理緩存的方法可以達到某個恆定值。 – ovgolovin

1

緩存的要點是減少函數的平均調用時間。與保留更多信息_cache相關的開銷並修剪它而不是清除它會增加平均通話時間。 _cache.clear()調用將會很快完成,即使你失去了緩存,這也比維護一個緩存狀態更有優勢,並且在達到限制時有一定的開銷,即從緩存中刪除單個元素。

有去想計算緩存效率時的幾件事情:

  1. 平均通話時間緩存命中(時間很短)
  2. 平均通話時間上高速緩存未命中(長)
  3. 頻率高速緩存命中次數(相當少見)
  4. 呼叫時,將清除緩存或修剪(很少見)時間

的問題是增加#3是否意味着增加#2和#4。我的猜測是,它沒有,或者差異可以忽略不計,保持代碼簡單是可取的。

+0

但保留更多信息的相關開銷'_cache'將是可預測的。但是,在零星時刻清理'_cache'將會使得緩存效率的評估非常麻煩,因爲它將非常可靠地解決'cache'被清除的時刻。 – ovgolovin

5

下面是一個關於高速緩存的新的regex模塊的開發者的報價,這是一個將新模塊與當前re模塊分開的功能列表的一部分。

7)修改re編譯表達式緩存以更好地處理 抖動條件。目前,編譯正則表達式時, 結果被緩存,因此如果再次編譯同一個表達式,則從緩存中檢索它,並且不需要執行額外的工作。這個 緩存最多支持100個條目。一旦達到第100個條目, 緩存被清除並且必須進行新的編譯。危險,所有這一切都是罕見的,是人們可能編譯第100個表達式只發現一個 重新編譯它,並且必須重新做同樣的工作,當它可能 已經完成3個表達式之前。通過略微修改這個邏輯,它可以建立一個任意的計數器,它給每個編譯條目賦予一個時間戳 ,而不是在它達到容量時清除整個緩存,只消除最舊的一半緩存,保留 一半是最近的。這應該限制 不斷重新編譯大量正則表達式爲 的情況。除此之外,我會將限制更新爲 256個條目,這意味着保留了最近的128條。

http://bugs.python.org/issue2636

這似乎表明,它更可能是解釋當前緩存行爲的開發商或「強調可讀性」的懶惰。

+0

我已經打開'regex'模塊的源代碼(我已經使用它約兩週了)。負責清除緩存的代碼塊如下所示:if len(_cache)> = _MAXCACHE:shrink_cache(_cache,_named_args,_MAXCACHE)'。但是我沒有在模塊上找到任何'shrink_cache'函數。沒有這樣的功能。 – ovgolovin

+0

儘管如此,作者還是堅持消除大塊緩存,而不是在需要時去除單個元素。新方法消除了最近一半的緩存。 – ovgolovin