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

Ton*_*ony 0 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
Run Code Online (Sandbox Code Playgroud)

我的问题是,为什么在创建稀疏矩阵时,当我删除最初仅占用200 Mb内存的原始numpy对象时,内存使用量会跃升至15 gb,而仅下降至3.1 Gb?

我怀疑这与正在使用的内存与已提交的内存有关,但正在努力理解该机制。

编辑:我正在Windows 10上运行此

Nil*_*ner 5

这与稀疏矩阵无关,而是与现代操作系统分配内存的方式有关:请求内存时,您的OS将立即返回一个地址,但实际上不会在物理内存中分配页面。仅在第一次触摸页面中的数据(读取或写入)时,才会分配每个页面。由于您仅设置了几个值,因此只会分配几个页面,而所有未被触摸的页面实际上都不在内存中。

通常将其显示为虚拟(VIRT)内存和物理(PHYS)内存。占用VIRT但不存在的所有内容PHYS都是已分配但尚未触及的内存。

您会看到内存消耗增加,因为将矩阵转换为sparse.csr_matrix要求SciPy读取整个数组。这又使您的操作系统分配所有页面并用零填充它们。

为了理解这一点,我们可以使用以下示例:在导入任何内容之前,我的ipython内核位于

# 2GB VIRT, 44MB PHYS
Run Code Online (Sandbox Code Playgroud)

我们分配内存,但用零填充,因此我们没有碰过任何东西。我们使用了大量VIRT但几乎没有PHYSRAM。

import numpy
array = numpy.zeros((10000, 50000))
# 6GB VIRT, 50MB PHYS
Run Code Online (Sandbox Code Playgroud)

在将对角线设置为随机值后,我们发现的值仅略有增加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
Run Code Online (Sandbox Code Playgroud)

如果现在我们突然计算数组的总和,则使用量会增加,因为读取内存会触发物理分配:

numpy.sum(array)
# 6GB VIRT, 4GB PHYS
Run Code Online (Sandbox Code Playgroud)

创建填充随机值的数组时也是如此:所有数组都会立即进行物理分配。

array = numpy.random.rand(10000, 50000)
# 6GB VIRT, 4GB PHYS
Run Code Online (Sandbox Code Playgroud)

这就是为什么建议直接以稀疏格式创建稀疏数组的原因:

import scipy.sparse
sparse_array = scipy.sparse.dok_matrix((10000, 50000))
# 2GB VIRT, 50MB PHYS
Run Code Online (Sandbox Code Playgroud)

DOK允许索引,因此我们可以高效地执行

sparse_array[numpy.arange(10000), numpy.arange(10000)] = numpy.random.rand(10000)
# 2GB VIRT, 54MB PHYS
Run Code Online (Sandbox Code Playgroud)

并允许有效转换为CSR:

csr_sparse_array = scipy.sparse.csr_matrix(sparse_array)
# 2GB VIRT, 54MB PHYS
Run Code Online (Sandbox Code Playgroud)

这些值是在OSX上计算得出的,但一般原理适用于Linux,OSX和Windows。