python - numpy.memmap : bogus memory allocation

标签 python linux numpy memory-mapped-files sparse-file

我有一个使用 numpy.memmap 数组操作的 python3 脚本。它将数组写入位于 /tmp 中新生成的临时文件:

import numpy, tempfile

size = 2 ** 37 * 10
tmp = tempfile.NamedTemporaryFile('w+')
array = numpy.memmap(tmp.name, dtype = 'i8', mode = 'w+', shape = size)
array[0] = 666
array[size-1] = 777
del array
array2 = numpy.memmap(tmp.name, dtype = 'i8', mode = 'r+', shape = size)
print('File: {}. Array size: {}. First cell value: {}. Last cell value: {}'.\
      format(tmp.name, len(array2), array2[0], array2[size-1]))
while True:
    pass

硬盘大小只有250G。尽管如此,它还是能以某种方式在/tmp 中生成10T 的大文件,并且相应的数组似乎仍然可以访问。脚本的输出如下:

File: /tmp/tmptjfwy8nr. Array size: 1374389534720. First cell value: 666. Last cell value: 777

文件确实存在并且显示为10T大:

$ ls -l /tmp/tmptjfwy8nr
-rw------- 1 user user 10995116277760 Dec  1 15:50 /tmp/tmptjfwy8nr

但是,/tmp 的整体大小要小得多:

$ df -h /tmp
Filesystem      Size  Used Avail Use% Mounted on
/dev/sda1       235G  5.3G  218G   3% /

进程也假装使用10T虚拟内存,这也是不可能的。 top 命令的输出:

  PID USER      PR  NI    VIRT    RES    SHR S  %CPU %MEM     TIME+ COMMAND 
31622 user      20   0 10.000t  16592   4600 R 100.0  0.0   0:45.63 python3

据我所知,这意味着在调用 numpy.memmap 期间,整个数组所需的内存未分配,因此显示的文件大小是虚假的。这反过来意味着,当我开始逐渐用我的数据填充整个数组时,有时我的程序会崩溃或我的数据会损坏。

确实,如果我在我的代码中引入以下内容:

for i in range(size):
    array[i] = i

一段时间后我收到错误:

Bus error (core dumped)

因此,问题:如何在开始时检查,是否真的有足够的内存来存放数据,然后确实为整个数组预留空间?

最佳答案

关于您生成 10 TB 文件的事实没有任何“虚假”

你要求的数组大小

2 ** 37 * 10 = 1374389534720 elements

'i8' 的 dtype 表示一个 8 字节(64 位)整数,因此您的最终数组的大小为

1374389534720 * 8 = 10995116277760 bytes

10995116277760 / 1E12 = 10.99511627776 TB


如果您只有 250 GB 的可用磁盘空间,那么如何创建“10 TB”文件?

假设您使用的是相当现代的文件系统,您的操作系统将能够生成几乎任意大的 sparse files ,无论您实际上是否有足够的物理磁盘空间来支持它们。

例如,在我的 Linux 机器上我可以做这样的事情:

# I only have about 50GB of free space...
~$ df -h /
Filesystem     Type  Size  Used Avail Use% Mounted on
/dev/sdb1      ext4  459G  383G   53G  88% /

~$ dd if=/dev/zero of=sparsefile bs=1 count=0 seek=10T
0+0 records in
0+0 records out
0 bytes (0 B) copied, 0.000236933 s, 0.0 kB/s

# ...but I can still generate a sparse file that reports its size as 10 TB
~$ ls -lah sparsefile
-rw-rw-r-- 1 alistair alistair 10T Dec  1 21:17 sparsefile

# however, this file uses zero bytes of "actual" disk space
~$ du -h sparsefile
0       sparsefile

尝试在 np.memmap 文件初始化后调用 du -h 以查看它实际使用了多少磁盘空间。

当您实际开始将数据写入您的 np.memmap 文件时,一切都会正常,直到您超过存储的物理容量,此时该过程将以 Bus 终止错误。这意味着如果您需要将 < 250GB 的数据写入 np.memmap 数组,那么可能没有问题(实际上这可能还取决于您在数组中写入的位置,以及无论是行专业还是列专业)。


一个进程怎么可能使用 10 TB 的虚拟内存?

当您创建 memory map ,内核在调用进程的虚拟地址空间中分配一个新的地址 block ,并将它们映射到磁盘上的一个文件。因此,您的 Python 进程使用的虚拟内存量将随着刚刚创建的文件的大小而增加。由于文件也可以是稀疏的,因此虚拟内存不仅可以超过可用 RAM 的总量,而且还可以超过计算机上的总物理磁盘空间。


如何检查是否有足够的磁盘空间来存储完整的 np.memmap 数组?

我假设您想在 Python 中以编程方式执行此操作。

  1. 获取可用磁盘空间量。 this previous SO question的答案中给出了多种方法.一种选择是 os.statvfs :

    import os
    
    def get_free_bytes(path='/'):
        st = os.statvfs(path)
        return st.f_bavail * st.f_bsize
    
    print(get_free_bytes())
    # 56224485376
    
  2. 以字节为单位算出数组的大小:

    import numpy as np
    
    def check_asize_bytes(shape, dtype):
        return np.prod(shape) * np.dtype(dtype).itemsize
    
    print(check_asize_bytes((2 ** 37 * 10,), 'i8'))
    # 10995116277760
    
  3. 检查是否 2. > 1.


更新是否有一种“安全”的方式来分配 np.memmap 文件,以保证保留足够的磁盘空间来存储整个阵列?

一种可能是使用 fallocate预先分配磁盘空间,例如:

~$ fallocate -l 1G bigfile

~$ du -h bigfile
1.1G    bigfile

您可以从 Python 调用它,例如使用 subprocess.check_call :

import subprocess

def fallocate(fname, length):
    return subprocess.check_call(['fallocate', '-l', str(length), fname])

def safe_memmap_alloc(fname, dtype, shape, *args, **kwargs):
    nbytes = np.prod(shape) * np.dtype(dtype).itemsize
    fallocate(fname, nbytes)
    return np.memmap(fname, dtype, *args, shape=shape, **kwargs)

mmap = safe_memmap_alloc('test.mmap', np.int64, (1024, 1024))

print(mmap.nbytes / 1E6)
# 8.388608

print(subprocess.check_output(['du', '-h', 'test.mmap']))
# 8.0M    test.mmap

我不知道使用标准库执行此操作的独立于平台的方法,但有一个 fallocate Python module on PyPI这应该适用于任何基于 Posix 的操作系统。

关于python - numpy.memmap : bogus memory allocation,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34023665/

相关文章:

python - Nextcord:无法访问服务器上的 ffmpeg

linux - 在 bash 中打印字母表

python - python可以处理assertRaises吗

python - json.loads() 返回一个 unicode 对象而不是一个字典

linux - 如何在 Mac 或 Linux 上的 .NET Core 上构建多目标解决方案?

python - 在python中获取每个月的年份

python - Numpy 和 16 位 PGM

python - allclose() 和 array_equal() 有什么区别?

python - 如何保持用户登录django

linux - 需要将 jenkins 日志文件推送到 Amazon S3 存储桶