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

Python 稀疏矩阵如何影响内存使用?,python,matrix,sparse-matrix,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 = (400

在下面的示例中,我创建了一个带有零的大
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上运行此操作,这与稀疏矩阵无关,但与现代操作系统分配内存的方式有关:请求内存时,操作系统将立即返回一个地址,但实际上不会在物理内存中分配页面。只有在第一次接触页面中的数据(读或写)时,才会分配每个单独的页面。因为您只设置了几个值,所以只会分配几个页面,所有未被触摸的页面实际上都不在内存中

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

由于将矩阵转换为稀疏。csr_矩阵需要SciPy读取整个数组,因此内存消耗会增加。这反过来会使操作系统分配所有页面并用零填充它们

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

# 2GB VIRT, 44MB PHYS
我们分配内存,但用零填充,所以我们什么都没碰。我们使用了大量的
VIRT
,但几乎没有
phy
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_sparse_array = scipy.sparse.csr_matrix(sparse_array)
# 2GB VIRT, 54MB PHYS

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

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

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

由于将矩阵转换为稀疏。csr_矩阵需要SciPy读取整个数组,因此内存消耗会增加。这反过来会使操作系统分配所有页面并用零填充它们

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

# 2GB VIRT, 44MB PHYS
我们分配内存,但用零填充,所以我们什么都没碰。我们使用了大量的
VIRT
,但几乎没有
phy
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_sparse_array = scipy.sparse.csr_matrix(sparse_array)
# 2GB VIRT, 54MB PHYS

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

您是在Linux还是Unix上?否,此示例是在Windows 10上生成的您是在Linux还是Unix上生成的?否,此示例是在Windows 10上生成的谢谢您的详细回答。我是否正确地假设物理
(PHYS)
内存可能对应于Windows任务管理器上显示的“正在使用的内存”,而虚拟
(VIRT)
内存对应于“已提交”内存?我注意到,在我的示例中,当创建带有零的初始矩阵时,虽然“正在使用的内存”为~3Gb,但提交的内存将提高到~15GB感谢您的详细响应。我是否正确地假设物理
(PHYS)
内存可能对应于Windows任务管理器上显示的“正在使用的内存”,而虚拟
(VIRT)
内存对应于“已提交”内存?我注意到,在我的示例中,当创建带有零的初始矩阵时,虽然“正在使用的内存”为~3Gb,但提交的内存却提高到~15Gb