[SciPy-user] how to crash scipy in 3s! (memory leak?)

David Cournapeau david at ar.media.kyoto-u.ac.jp
Mon May 28 21:19:07 EDT 2007


Nicolas Chopin wrote:
>     Hi,
> the program below eats all the memory of my computer
> in approx. 3 seconds, making it pretty unstable.
>
> from scipy import *
> def f(a):
>     return rand(1)+a
>
> n = 1e8
> for i in range(n):
>     x += f(0.1)
>
> It looks like a problem with rand()? e.g. rand() forgets
> to free memory? Or am I doing something "forbidden"?
> like returning an array, which creates a reference
> which is never deleted?
> In this case, what is the correct way to do something like
> def f(x):
>     return some_function_of(x, rand(1) )
>
> I use Ubuntu Feisty, Scipy 0.5.2, Numpy 1.01, Python 2.5.1
> (all 3 from Ubuntu repositories).
If it is crashing, it is a bug. If it is eating all your memory, then it 
is working fine :) First, doing loop as you do in an interpreted 
language (like python) is inherently slow for several reasons: don't do 
it. You could say the whole point of numpy is to give you abstractions 
to avoid looping like your doing.

If I understand correctly, you want to create n random scalars, and sum 
them up, right ? First, instead of calling n times rand(1), you can call 
rand(n), which will returns n random values. Then, instead of 
accumulating in a loop, you should first create the array of all 
intermediate values, and then summing the whole array (eg you create a 
temporary array tmp where tmp[i] contains the i-th call of f(0.1)):

"""
import numpy as N
from scipy import rand

def f(a):
    return rand(len(a)) + a

tmp = f(0.1 * N.ones(n))
x = N.sum(a)
"""

(I didn't check whether this code is running). Basically, in 
scipy/numpy, you should avoid using loop as much as possible, and try to 
"vectorize" as much as possible. Most numpy/scipy functions can be used like

numfunc(sequence)

instead of

for i in sequence:
    numfunc(i)

Concrete example:

from scipy import rand

def numfunc(n):
    a = rand(n)
    return numpy.sum(a)

def pyfunc(n):
    x = 0
    for i in xrange(n):
        x += rand(1)
    return x

numfunc is already 200 times faster for n = 1e4 on my computer, and the 
difference will grow for bigger size. It requires some thinking if you 
are not used to it. But if you really need looping with around 1e8 
elements, you won't be able to do it efficiently otherwise (that is if 
you stay in python).

Technically, the difference is that in the first case, the loop is done 
in numpy, which is optimized for this kind of things, and the second one 
is done entirely in python, and loops with functions calls are extremely 
slow compared to compiled language (several order of magnitudes most of 
the cases; this is actually not always true, there are some techniques 
to optimize this kind of thing, but this would take use way beyond the 
point of this thread).

Using xrange as Matthieu suggested would solve the memory part: range(n) 
allocates a list of n integers -> 1e8 * 4 bytes minimum if python is 
using 32 bits integers, I don't remember, and whereas xrange(n) creates 
the new iteration value at each iteration.

David



More information about the SciPy-User mailing list