稀疏矩阵如何影响内存使用?

时间:2017-05-15 12:27:24

标签: python matrix sparse-matrix

在下面的示例中,我创建了一个带有零的大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上运行它

1 个答案:

答案 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。