在下面的示例中,我创建了一个带有零的大numpy
对象,在对角线上放置一个随机数,然后转换为scipy
稀疏矩阵。我的内存使用情况报告来自任务管理器。
>>> import sys, random
>>> import numpy as np
>>> from scipy import sparse
## Memory in use at this point: 3.1 Gb
>>> m = np.zeros(shape = (40000, 40000), dtype = float)
>>> sys.getsizeof(m)
12800000112
## Memory in use at this point: 3.3 Gb
>>> for i in range(40000):
m[i][i] = round(random.random(),3)
>>> sys.getsizeof(m)
12800000112
## Memory in use at this point: 3.3 Gb
>>> mSp = sparse.csr_matrix(m)
>>> sys.getsizeof(mSp)
56
## Memory in use at this point: 14.9 Gb
>>> del m
## Memory in use at this point: 3.1 Gb
我的问题是,为什么在创建稀疏矩阵期间,当我删除最初只占用200 Mb内存的原始numpy对象时,内存使用量会跳至15 gb并且仅下降到3.1 Gb?
我怀疑这与使用的内存与已提交的内存有关,但我正在分散以理解机制。
编辑:我在Windows 10上运行它
答案 0 :(得分:3)
这与稀疏矩阵无关,但与现代操作系统分配内存的方式有关:当请求内存时,您的操作系统将立即返回一个地址,但实际上不会在物理内存中分配页面。只有在第一次触摸页面中的数据(读取或写入)时,才会分配每个页面。由于您只设置了几个值,因此只会分配几个页面,而所有未触摸的页面实际上都不在内存中。
这通常显示为虚拟(VIRT
)内存和物理(PHYS
)内存。在VIRT
但在PHYS
中不存在的所有内容都是已分配的内存,但尚未被触及。
您看到内存消耗增加,因为将矩阵转换为sparse.csr_matrix
需要SciPy读取整个数组。这反过来又使您的操作系统分配所有页面并用零填充它们。
要理解这一点,我们可以使用以下示例:在导入任何内容之前,我的ipython内核位于
# 2GB VIRT, 44MB PHYS
我们分配内存但用零填充,所以我们没有触及任何东西。我们使用了很多VIRT
但几乎没有PHYS
RAM。
import numpy
array = numpy.zeros((10000, 50000))
# 6GB VIRT, 50MB PHYS
在使用随机值设置对角线后,我们只看到PHYS
略有增加,因为我们的大部分页面实际上还没有实际存在于RAM中。
# this is a more efficient way of setting the main diagonal of your array, by the way
array[numpy.arange(10000), numpy.arange(10000)] = numpy.random.rand(10000)
# 6GB VIRT, 90MB PHYS
如果我们现在突然计算数组的总和,则使用量会增加,因为读取内存会触发一个物理分配:
numpy.sum(array)
# 6GB VIRT, 4GB PHYS
在创建一个充满随机值的数组时也一样:所有这些都是立即物理分配的。
array = numpy.random.rand(10000, 50000)
# 6GB VIRT, 4GB PHYS
这就是为什么建议直接以稀疏格式创建稀疏数组的原因:
import scipy.sparse
sparse_array = scipy.sparse.dok_matrix((10000, 50000))
# 2GB VIRT, 50MB PHYS
DOK允许索引,因此我们可以有效地执行
sparse_array[numpy.arange(10000), numpy.arange(10000)] = numpy.random.rand(10000)
# 2GB VIRT, 54MB PHYS
并允许有效转换为CSR:
csr_sparse_array = scipy.sparse.csr_matrix(sparse_array)
# 2GB VIRT, 54MB PHYS
这些值是在OSX上计算的,但一般原则适用于Linux,OSX和Windows。