节约内存的方法
在python中,经常会有大量数据的计算,一般都通过多维数组来存储,或者一些库称之为张量。在读取大量数据的时候,有可能会遇到内存不足的情况,今天了解了一下对多维数组是如何分配内存的,又该如何节约内存。
主要通过测试python list
,numpy
,pytorch
,对比这几个之间是否有区别。在做之前,自己的判断是没有区别的,先猜测在验证。
- python list
a = [[i for i in range(0, 4)] for j in range(3)]
b = [[i for i in range(4, 8)] for j in range(3)]
print(id(a))
print(id(b))
a = a + b
print(id(a))
a += b
print(id(a))
a[:] = a + b
print(id(a))
140434968054720
140434968053376
140434968099968
140434968099968
140434968099968
从上面的输出结果可以看到,刚开始赋值的时候a
的id
为4720,b
的id
为3376;
然后开始对a和b做加法操作,再赋值给a,结果a的id变成了9968,说明a = a + b
,是将a + b
的结果保存在新的内存中,再使a指向这个新内存地址;
接着计算a += b
,a的id是9968,与计算之前的id相同,我们通常都说a += b等价于a = a + b,但是实际上只是结果等价,a += b可没有将a指向新的内存;
最后是使用切片的方式,id与计算之前相同。
- numpy
import numpy as np
# 在notebook中注意restart kernel
a = np.arange(12).reshape(3, -1)
b = np.arange(12).reshape(3, -1)
print(id(a))
print(id(b))
a = a + b
print(id(a))
a += b
print(id(a))
a[:] = a + b
print(id(a))
139670777306928
139670777331920
139670777332000
139670777332000
139670777332000
- pytorch
import torch
a = torch.arange(12).reshape(3, -1)
b = torch.arange(12).reshape(3, -1)
print(id(a))
print(id(b))
a = a + b
print(id(a))
a += b
print(id(a))
a[:] = a + b
print(id(a))
139881180034624
139881313662592
139881313683840
139881313683840
139881313683840
可见numpy和pytorch与python list的内存分配时一直的。
注意:
在python list中,对两个list做+
运算,其实时对两个list进行拼接,并且- * /
等操作是不能list对list操作的;
numpy和pytorch是按元素(elementwise)进行操作的,当两个多维数据的shape不相等时会调用广播机制(broadcasting mechanism)来执行按元素操作。
a = [[i for i in range(0, 4)] for j in range(3)]
b = [[i for i in range(4, 8)] for j in range(3)]
c = a
print(id(a), id(c))
print(a), print(c)
a += b
print(id(a), id(c))
print(a), print(c)
139909205242624 139909205242624
[[0, 1, 2, 3], [0, 1, 2, 3], [0, 1, 2, 3]]
[[0, 1, 2, 3], [0, 1, 2, 3], [0, 1, 2, 3]]
139909205242624 139909205242624
[[0, 1, 2, 3], [0, 1, 2, 3], [0, 1, 2, 3], [4, 5, 6, 7], [4, 5, 6, 7], [4, 5, 6, 7]]
[[0, 1, 2, 3], [0, 1, 2, 3], [0, 1, 2, 3], [4, 5, 6, 7], [4, 5, 6, 7], [4, 5, 6, 7]]
原地操作
import numpy as np
a = np.arange(12).reshape(3, -1)
b = np.arange(12).reshape(3, -1)
c = a
print('id a:', id(a), 'id c:', id(c))
print('a: \n', a), print('c: \n', c)
a = a + b
print('id a:', id(a), 'id c:', id(c))
print('a: \n', a), print('c: \n', c)
id a: 139909204801616 id c: 139909204801616
a:
[[ 0 1 2 3]
[ 4 5 6 7]
[ 8 9 10 11]]
c:
[[ 0 1 2 3]
[ 4 5 6 7]
[ 8 9 10 11]]
id a: 139912994696288 id c: 139909204801616
a:
[[ 0 2 4 6]
[ 8 10 12 14]
[16 18 20 22]]
c:
[[ 0 1 2 3]
[ 4 5 6 7]
[ 8 9 10 11]]
我们使用c = a
其实就是将c指向id(a),当我们计算a = a + b
时,由于a指向了新的内存地址,a的值更新了,但这并不会影响c的值。如果需要c的值跟着a变化,则可以使用原地操作:
import numpy as np
a = np.arange(12).reshape(3, -1)
b = np.arange(12).reshape(3, -1)
c = a
print('id a:', id(a), 'id c:', id(c))
print('a: \n', a), print('c: \n', c)
a[:] = a + b
print('id a:', id(a), 'id c:', id(c))
print('a: \n', a), print('c: \n', c)
a += b
print('id a:', id(a), 'id c:', id(c))
print('a: \n', a), print('c: \n', c)
id a: 139909204802336 id c: 139909204802336
a:
[[ 0 1 2 3]
[ 4 5 6 7]
[ 8 9 10 11]]
c:
[[ 0 1 2 3]
[ 4 5 6 7]
[ 8 9 10 11]]
id a: 139909204802336 id c: 139909204802336
a:
[[ 0 2 4 6]
[ 8 10 12 14]
[16 18 20 22]]
c:
[[ 0 2 4 6]
[ 8 10 12 14]
[16 18 20 22]]
id a: 139909204802336 id c: 139909204802336
a:
[[ 0 3 6 9]
[12 15 18 21]
[24 27 30 33]]
c:
[[ 0 3 6 9]
[12 15 18 21]
[24 27 30 33]]