Ticket #11521: trac_11521_homset_weakcache_combined.patch

File trac_11521_homset_weakcache_combined.patch, 6.8 KB (added by SimonKing, 8 years ago)

Use the weak TripleDict? from #715 for the cache of homsets. Includes the reviewer patch

  • sage/categories/homset.py

    # HG changeset patch
    # User Simon King <simon.king@uni-jena.de>
    # Date 1325197817 -3600
    # Node ID 56d6dbbf6d7258a308383a46ec18feb52ce02eeb
    # Parent  e22bf726df125e6f6fa4e63dad7799d7d57c1533
    #11521: Use the weak TripleDict from #715 for the cache of homsets.
    Includes reviewer patch
    
    diff --git a/sage/categories/homset.py b/sage/categories/homset.py
    a b  
    1 """
     1r"""
    22Homsets
    33
     4The class :class:`Hom` is the base class used to represent sets of morphisms
     5between objects of a given category.
     6:class:`Hom` objects are usually "weakly" cached upon creation so that they
     7don't have to be generated over and over but can be garbage collected together
     8with the corresponding objects when these are are not stongly ref'ed anymore.
     9
     10EXAMPLES:
     11
     12In the following, the :class:`Hom` object is indeed cached::
     13
     14    sage: K = GF(17)
     15    sage: H = Hom(ZZ, K)
     16    sage: H
     17    Set of Homomorphisms from Integer Ring to Finite Field of size 17
     18    sage: H is Hom(ZZ, K)
     19    True
     20
     21Nonetheless, garbage collection occurs when the original references are
     22overwritten::
     23
     24    sage: for p in prime_range(200):
     25    ...     K = GF(p)
     26    ...     H = Hom(ZZ, K)
     27    ...
     28    sage: import gc
     29    sage: _ = gc.collect()
     30    sage: from sage.rings.finite_rings.finite_field_prime_modn import FiniteField_prime_modn as FF
     31    sage: L = [x for x in gc.get_objects() if isinstance(x, FF)]
     32    sage: len(L)
     33    2
     34    sage: L
     35    [Finite Field of size 2, Finite Field of size 199]
     36
    437AUTHORS:
    538
    639- David Kohel and William Stein
     
    1043- William Stein (2006-01-14): Changed from Homspace to Homset.
    1144
    1245- Nicolas M. Thiery (2008-12-): Updated for the new category framework
     46
     47- Simon King (2011-12): Use a weak cache for homsets
    1348"""
    1449
    1550#*****************************************************************************
     
    2762#                  http://www.gnu.org/licenses/
    2863#*****************************************************************************
    2964
    30 import weakref
    31 
    3265from sage.categories.category import Category
    3366import morphism
    3467from sage.structure.parent import Parent, Set_generic
     
    3669from sage.misc.cachefunc import cached_function
    3770import types
    3871
    39 _cache = {}
     72###################################
     73# Use the weak "triple" dictionary
     74# introduced in trac ticket #715
     75
     76import weakref
     77from sage.structure.coerce_dict import TripleDict
     78_cache = TripleDict(53)
     79
    4080def Hom(X, Y, category=None):
    4181    """
    4282    Create the space of homomorphisms from X to Y in the category ``category``.
     
    72112        sage: Hom(FreeModule(QQ,1), FreeModule(ZZ,1))
    73113        Set of Morphisms from Vector space of dimension 1 over Rational Field to Ambient free module of rank 1 over the principal ideal domain Integer Ring in Category of vector spaces over Rational Field
    74114
     115    Here, we test against a memory leak that has been fixed at :trac:`11521` by
     116    using a weak cache::
     117
     118        sage: for p in prime_range(10^5):
     119        ...    K = GF(p)
     120        ...    a = K(0)
     121        sage: import gc
     122        sage: gc.collect()       # random
     123        624
     124        sage: from sage.rings.finite_rings.finite_field_prime_modn import FiniteField_prime_modn as FF
     125        sage: L = [x for x in gc.get_objects() if isinstance(x, FF)]
     126        sage: len(L), L[0], L[len(L)-1]
     127        (2, Finite Field of size 2, Finite Field of size 99991)
     128
    75129    To illustrate the choice of the category, we consider the
    76130    following parents as running examples::
    77131
     
    155209    # However it breaks somehow the coercion (see e.g. sage -t sage.rings.real_mpfr)
    156210    # To be investigated.
    157211    global _cache
    158     key = (X,Y,category)
    159     if _cache.has_key(key):
     212    cat_ref = weakref.ref(category) if category is not None else None
     213    key = (X,Y,cat_ref)
     214    try:
    160215        H = _cache[key]()
    161         # What is this test for? Why does the cache ever contain a 0 value?
    162         # This actually occurs: see e.g. sage -t  sage-combinat/sage/categories/modules_with_basis.py
    163         if H:
    164             # Are domain or codomain breaking the unique parent condition?
    165             if H.domain() is X and H.codomain() is Y:
    166                 return H
     216    except KeyError:
     217        H = None
     218    if H:
     219        # Are domain or codomain breaking the unique parent condition?
     220        if H.domain() is X and H.codomain() is Y:
     221            return H
    167222
    168223    try:
     224        # Apparently X._Hom_ is supposed to be cached
    169225        return X._Hom_(Y, category)
    170226    except (AttributeError, TypeError):
    171227        pass
     
    182238    else:
    183239        raise TypeError, "Argument category (= %s) must be a category."%category
    184240    # Now, as the category may have changed, we try to find the hom set in the cache, again:
    185     key = (X,Y,category)
    186     if _cache.has_key(key):
     241    cat_ref = weakref.ref(category) if category is not None else None
     242    key = (X,Y,cat_ref)
     243    try:
    187244        H = _cache[key]()
    188         if H:
    189             # Are domain or codomain breaking the unique parent condition?
    190             if H.domain() is X and H.codomain() is Y:
    191                 return H
     245    except KeyError:
     246        H = None
     247    if H:
     248        # Are domain or codomain breaking the unique parent condition?
     249        if H.domain() is X and H.codomain() is Y:
     250            return H
    192251
    193252    # coercing would be incredibly annoying, since the domain and codomain
    194253    # are totally different objects
     
    201260    H = category.hom_category().parent_class(X, Y, category = category)
    202261           
    203262    ##_cache[key] = weakref.ref(H)
    204     _cache[(X, Y, category)] = weakref.ref(H)
     263    _cache[key] = weakref.ref(H)
    205264    return H
    206265
    207266def hom(X, Y, f):
  • sage/libs/singular/ring.pyx

    diff --git a/sage/libs/singular/ring.pyx b/sage/libs/singular/ring.pyx
    a b  
    443443    EXAMPLE::
    444444
    445445        sage: import gc
     446        sage: _ = gc.collect()
    446447        sage: from sage.rings.polynomial.multi_polynomial_libsingular import MPolynomialRing_libsingular
    447448        sage: from sage.libs.singular.groebner_strategy import GroebnerStrategy
    448449        sage: from sage.libs.singular.ring import ring_refcount_dict
  • sage/rings/polynomial/multi_polynomial_libsingular.pyx

    diff --git a/sage/rings/polynomial/multi_polynomial_libsingular.pyx b/sage/rings/polynomial/multi_polynomial_libsingular.pyx
    a b  
    402402            sage: import gc
    403403            sage: from sage.rings.polynomial.multi_polynomial_libsingular import MPolynomialRing_libsingular
    404404            sage: from sage.libs.singular.ring import ring_refcount_dict
     405            sage: gc.collect()  # random output
    405406            sage: n = len(ring_refcount_dict)
    406407            sage: R = MPolynomialRing_libsingular(GF(547), 2, ('x', 'y'), TermOrder('degrevlex', 2))
    407408            sage: len(ring_refcount_dict) == n + 1