Skip to content

[probably fixed upstream] memory leak when converting between GMP-based types and numpy types #2383

@craigcitro

Description

@craigcitro

There's a fairly large memory leak that shows up when converting from ZZ and QQ to numpy types. Here's an example:

sage: ls = range(50000)
sage: import numpy
sage: A.<x> = ZZ[]
sage: f = x**3-2*x+1
sage: type(f)
<type 'sage.rings.polynomial.polynomial_integer_dense_ntl.Polynomial_integer_dense_ntl'>

sage: def my_func(f):
    tmp = numpy.atleast_1d(f.list())
    tmp2 = tmp.astype(float)
....:     
sage: get_memory_usage()
'124M+'
sage: for _ in ls: my_func(f)
....: 
sage: get_memory_usage()
'133M+'


sage: f = f.change_ring(QQ)
sage: type(f)
<class 'sage.rings.polynomial.polynomial_element_generic.Polynomial_rational_dense'>

sage: get_memory_usage()
'133M+'

sage: for _ in ls: my_func(f)
....: 
sage: get_memory_usage()
'150M+'

sage: f = f.change_ring(RDF)
sage: get_memory_usage()
'150M+'

sage: for _ in ls: my_func(f)
....: 
sage: get_memory_usage()
'150M+'

This was first noted in trac #2239, where we use numpy.roots(f.list()) instead of the above, but I think it ultimately gets traced to the code above. As you can see, it leaks for both ZZ and QQ, but not for RDF -- or most other types one might try. However, since RR uses GMP, it leaks too:

sage: ls = range(50000)ent Mercurial branch is: abvar
sage: import numpy
sage: B.<x> = RR[]
sage: f = x**3-2*x+1
sage: type(f)
<class 'sage.rings.polynomial.polynomial_element_generic.Polynomial_generic_dense_field'>

sage: def my_func(f):
....:     tmp = numpy.atleast_1d(f.list())
....:     tmp2 = tmp.astype(float)
....:     
sage: def my_func2(f):
    tmp = numpy.atleast_1d([ RR(a) for a in f.list() ])
    tmp2 = tmp.astype(float)
....:     
sage: get_memory_usage()
'124M+'

sage: for _ in ls: my_func(f)
....: 
sage: get_memory_usage()
'124M+'

sage: for _ in ls: my_func2(f)
....: 
sage: get_memory_usage()
'138M+'

There's a known workaround -- don't convert directly between these types. For example:

sage: def my_func2(f):
    tmp = numpy.atleast_1d([ int(a) for a in f.list() ])
    tmp2 = tmp.astype(float)
....:     
sage: get_memory_usage()
'150M+'

sage: for _ in ls: my_func2(f)
....: 
sage: get_memory_usage()
'150M+'

Several people looked at this during SD8, but didn't come up with a solution. If anyone hits on anything, please let us know!

Component: memleak

Issue created by migration from https://trac.sagemath.org/ticket/2383

Metadata

Metadata

Type

No type

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions