Does numpy allocate new matrices for every operation you perform on a matrix?
For example:
A = np.random.rand(10, 20)
A = 2 * A # Operation 1: Is a copy of A made, and a reference assigned to A?
B = 2 * A # Operation 2: Does B get a completely different copy of A?
C = A # Operation 3: Does C get a reference to A?
And slice operations:
A[0, :] = 3
How about chained operations?
D = A * B * C # Elementwise multiplication, if A * B allocates memory, does
# (A * B) * C allocate another patch of memory?
Numpy's a fantastic library, but I just want to know what happens under the hood. My intuition says that slice operations modify the memory view in place, but I don't know about assignments.
The size in memory of numpy arrays is easy to calculate. It's simply the number of elements times the data size, plus a small constant overhead. For example, if your cube. dtype is int64 , and it has 1,000,000 elements, it will require 1000000 * 64 / 8 = 8,000,000 bytes (8Mb).
NumPy uses much less memory to store dataThe NumPy arrays takes significantly less amount of memory as compared to python lists. It also provides a mechanism of specifying the data types of the contents, which allows further optimisation of the code.
Less memory usage: The Python NumPy array consumes less memory than lists. Less execution time: The NumPy array is pretty fast in terms of execution, as compared to lists in Python.
NumPy is fast because it can do all its calculations without calling back into Python. Since this function involves looping in Python, we lose all the performance benefits of using NumPy. For a 10,000,000-entry NumPy array, this functions takes 2.5 seconds to run on my computer.
Keep in mind that a numpy array is a Python object. Python creates and deletes objects continually. The array has attributes shown in the .FLAGS
and .__array_interface__
dictionaries, things like the shape
and dtype
. The attribute that takes up (potentially) a lot of memory is the data buffer. It may be a few bytes long, or may be MB.
Where possible numpy operations try to avoid copying the data buffer. When indexing, it will return a view
if possible. I think the documentation compares views and copies well enough.
But views are different from Python references. A shared reference means two variables (or pointers in a list or dictionary) point to the same Python object. A view
is a different array object, but one which shares the data buffer with another array. A copy has its own data buffer.
In your examples:
A = np.random.rand(10, 20)
A
is a variable pointing to an array object. That object has a data buffer with 200 floats (200*8 bytes).
A = 2 * A # Operation 1: Is a copy of A made, and a reference assigned to A?
2*A
creates a new object, with a new data buffer. None of its data values can be shared with the original A
. A=...
reassigns the A
variable. The old A
object is 'lost', and eventually memory is garbage collected.
B = 2 * A # Operation 2: Does B get a completely different copy of A?
This 2*A
operates on the new A
array. The object is assigned to B
. A
remains unchanged.
C = A # Operation 3: Does C get a reference to A?
Yes, this is just normal Python assignment. C
refers to the same object as A
. id(C)==id(A)
.
B = A[1,:] # B is a view
B
is a reference to a new array object. But that object shares the data buffer with A
. That's because the desired values can be found in the buffer by just starting at a different point, and using a different shape
.
A[0, :] = 3
This LHS slice will change a subset of the values of A
. It is similar to:
B = A[0, :]
B = 3
But there are subtile differences betwee LHS and RHS slices. On the LHS you have to pay more attention to when you get a copy as opposed to a view. I've seen this especially with expressions like A[idx1,:][:,idx2] = 3
.
D = A * B * C
The details of how many intermediate copies are made in a calculation like this are buried in the numpy C code. It's safest to assume that it does something like:
temp1 = A*B
temp2 = temp1*C
D = temp2
(temp1 goes to garbage)
For ordinary calculations it isn't worth worrying about those details. If you are really pushing for speed you could do a timeit
on alternatives. And occasionally we get SO questions about operations giving memory errors
. Do a search to get more details on those.
Yes it creates new arrays. Except C. C and A point to same memory.
You can test all of this yourself. Try the id(A)
command to see where in memory A is pointing. Also, just create a smaller structure and modify parts of it and then see if A, B, and/or C are also updated.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With