我有一个python3
使用numpy.memmap
数组操作的脚本.它将数组写入新生成的临时文件,该文件位于/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.尽管如此,它可以以某种方式生成10T大文件/tmp
,并且相应的数组似乎仍然可以访问.脚本的输出如下:
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)
因此,问题是:如何在开头检查,如果有足够的内存用于数据,然后确实为整个阵列保留了空间?
你要的是大小数组
2**37*10 = 1374389534720元素
dtype 'i8'
表示8字节(64位)整数,因此最终数组的大小为
1374389534720*8 = 10995116277760字节
要么
10995116277760/1E12 = 10.99511627776 TB
假设您正在使用一个相当现代的文件系统,您的操作系统将能够生成几乎任意大的稀疏文件,无论您是否确实有足够的物理磁盘空间来支持它们.
例如,在我的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
尝试调用du -h
您的np.memmap
文件时,它已经被初始化看到它使用多少实际的磁盘空间之后.
当您开始实际将数据写入np.memmap
文件时,一切都会正常,直到您超出存储的物理容量,此时进程将以a终止Bus error
.这意味着如果你需要向np.memmap
数组写入<250GB的数据,那么可能没有问题(实际上这可能还取决于你在数组中写入的位置,以及它是行还是列主要).
创建内存映射时,内核会在调用进程的虚拟地址空间内分配一个新的地址块,并将它们映射到磁盘上的文件.因此,Python进程使用的虚拟内存量将增加刚刚创建的文件的大小.由于文件也可以是稀疏的,因此虚拟内存不仅可以超过可用的RAM总量,而且还可以超过计算机上的总物理磁盘空间.
np.memmap
阵列?我假设您想在Python中以编程方式执行此操作.
获取可用的可用磁盘空间量.在此前的SO问题的答案中给出了各种方法.一种选择是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
以字节为单位计算出数组的大小:
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
检查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
PyPI上有一个Python模块,适用于任何基于Posix的操作系统.