2012-03-13 7 views
5

De acuerdo con la documentación oficial de Python para el módulo weakref, "el uso principal para referencias débiles es implementar cachés o mapeos con objetos grandes, ...". Entonces, utilicé un WeakValueDictionary para implementar un mecanismo de caché para una función de larga ejecución. Sin embargo, como se vio después, los valores en el caché nunca permanecieron allí hasta que realmente se usarían de nuevo, pero necesitaban ser recalculados casi siempre. Como no había referencias sólidas entre los accesos a los valores almacenados en el WeakValueDictionary, el GC se deshizo de ellos (aunque no hubo absolutamente ningún problema con la memoria).Problemas con el GC al usar un WeakValueDictionary para cachés

Ahora, ¿cómo se supone que debo usar las cosas de referencia débiles para implementar un caché? Si mantengo referencias sólidas en algún lugar explícitamente para evitar que el GC elimine mis referencias débiles, no tendría sentido utilizar un Divalista de Débil en primer lugar. Probablemente debería haber alguna opción para el GC que lo indique: elimine todo lo que no tenga referencias y todo con referencias débiles solo cuando la memoria se está agotando (o se excede algún umbral). ¿Hay algo como eso? ¿O hay una mejor estrategia para este tipo de caché?

Respuesta

3

Voy a intentar responder a su consulta con un ejemplo de cómo usar el módulo weakref para implementar el almacenamiento en caché. Mantendremos las referencias débiles de nuestro caché en weakref.WeakValueDictionary, y las referencias fuertes en un collections.deque porque tiene una propiedad maxlen que controla la cantidad de objetos a los que se aferra. Implementado en el estilo de cierre de función:

import weakref, collections 
def createLRUCache(factory, maxlen=64): 
    weak = weakref.WeakValueDictionary() 
    strong = collections.deque(maxlen=maxlen) 

    notFound = object() 
    def fetch(key): 
     value = weak.get(key, notFound) 
     if value is notFound: 
      weak[key] = value = factory(key) 
     strong.append(value) 
     return value 
    return fetch 

El objeto deque sólo mantendrá los últimos maxlen entradas, simplemente dejando caer las referencias a las antiguas entradas una vez que alcanza la capacidad. Cuando las antiguas entradas se eliminan y la basura recogida por Python, el WeakValueDictionary eliminará esas claves del mapa. Por lo tanto, la combinación de los dos objetos nos ayuda a mantener solo maxlen entradas en nuestro caché LRU.

class Silly(object): 
    def __init__(self, v): 
     self.v = v 

def fib(i): 
    if i > 1: 
     return Silly(_fibCache(i-1).v + _fibCache(i-2).v) 
    elif i: return Silly(1) 
    else: return Silly(0) 
_fibCache = createLRUCache(fib) 
0

Parece que no hay forma de superar esta limitación, al menos en CPython 2.7 y 3.0.

Reflexionando sobre createLRUCache solución de():

La solución con createLRUCache (fábrica, maxlen = 64) no está muy bien con mis expectativas. La idea de enlazar a 'maxlen' es algo que me gustaría evitar. Me obligaría a especificar aquí alguna constante no escalable o crear alguna heurística, para decidir qué constante es mejor para este o para los límites de la memoria del host.

yo preferiría GC eliminará los valores referenciados a partir WeakValueDictionary no de inmediato, pero en condition is used for regular GC:

Cuando el número de asignaciones menos el número de cancelaciones de asignación supera threshold0, colección se inicia.

Cuestiones relacionadas