numpy array - 从磁盘逐行加载 - 内存效率高但速度快

numpy array - load from disk row by row - memory efficiency but fast

有没有办法从磁盘流水线化 numpy 数组,以这种方式保存

np.save('data.npy',np.zeros(shape=[500,300,3])) # RGB image

并以类似于生成器工作的方式逐行(或逐列)读取,但没有加载延迟?


详细说明

我的应用程序需要接近零延迟,但从磁盘加载更大的阵列可能需要一些时间 (~0.02-0.1s)。即使是这么小的延迟也会产生不愉快的结果。

我有满足速度的解决方案:

dictionary = {'array1': array1, ....}

有了这个我可以立即访问数组,但是因为我使用的是 raspberry pi 零,所以我的 python 程序受 CPU 和 RAM 的限制,所以如果我有很多数组,我会处理

MemoryError

我的应用程序以 50hz 的频率逐行读取数组,就像这样

for row in array:
    [operation with row]
    time.sleep(0.02) # in reality, whole cycle is 0.02s ( including operation time) 

我正在寻找发电机的种类:

def generate_rows(path):
    array = np.load(path)
    for row in array:
        yield row

这解决了内存问题,但我猜测我将失去接近零延迟(加载数组)。

因此我的问题是:有没有一种方法可以像使用生成器一样生成行,但是第一行已经准备好可以说 'immediately',延迟几乎为零?


编辑:基于@Lukas Koestler 和@hpaulj 的评论,我尝试了 memmap,但结果出人意料地不好,因为 memmap 在内存上崩溃比简单地加载完整数组更快。

WINDOWS 10

我在磁盘上保存了 1000 个 numpy 数组 (shape = [500,30,3]) 并尝试使用 np.load 和 np.load 以及 memmap read

来缓存它们
import numpy as np
import os

mats = os.listdir('matrixes')
cache = []
for i in range(10):
    for n in mats:
        cache.append(np.load('matrixes\{}'.format(n),mmap_mode='r')) # Load with memmap
        #cache.append(np.load('matrixes\{}'.format(n))) #load without memmap

    print('{} objects stored in cache '.format((i+1)*1000))

在 运行 两种变体(有 memmap 和没有 memmap)之后,都发生了这两个错误

存储 4000 个内存映射对象后的内存映射:

...
  File "C:\Python27\lib\site-packages\numpy\core\memmap.py", line 264, in __new__
    mm = mmap.mmap(fid.fileno(), bytes, access=acc, offset=start)
WindowsError: [Error 8] Not enough memory resources are available to process this command

缓存 5000 后没有 memmap 的简单 np.load np.arrays

....
File "C:\Python27\lib\site-packages\numpy\lib\format.py", line 661, in read_array
    array = numpy.fromfile(fp, dtype=dtype, count=count)
MemoryError 

Raspberry pi零

正如@Alex Yu 所指出的,我在 windows 10 上测试,切换到 raspberry pi 零,

我得到了超过 1000 个 numpy 数组(花了很长时间)然后我得到了

1000 objects stored in cache
Killed

使用 Memmaps,我很快就超过了 1000 个 memmaps,但我遇到了不同的错误

File "/usr/lib/python2.7/dist-packages/numpy/lib/npyio.py", line 416, in load
    return format.open_memmap(file, mode=mmap_mode)
  File "/usr/lib/python2.7/dist-packages/numpy/lib/format.py", line 792, in open_memmap
    mode=mode, offset=offset)
  File "/usr/lib/python2.7/dist-packages/numpy/core/memmap.py", line 264, in __new__
    mm = mmap.mmap(fid.fileno(), bytes, access=acc, offset=start)
mmap.error: [Errno 24] Too many open files

如果我没记错的话,这个错误发生在打开很多文件但没有关闭它们的时候。

Thanks to @Lukas Koestler and @hpaulj for directing me into using memmap

and Thanks to @Alex Yu for making the solution reality


我自己的问题的解决方案

使用

np.load(path,mmap_mode='r')

有效,但受打开文件的限制。在 windows 和 Linux 上抛出不同的错误:

获胜

WindowsError: [Error 8] Not enough memory resources are available to process this command

mmap.error: [Errno 24] Too many open files

这已通过 @Alex Yu extend limit of opened files 给出的 link 解决。

摘录:

打开

/etc/security/limits.conf

将以下内容粘贴到最后:

*         hard    nofile      500000
*         soft    nofile      500000
root      hard    nofile      500000
root      soft    nofile      500000

提取结束

仍然有限制,但它增加了列表中最多 8000 个对象的数量

...
8000 objects stored in cache

直到

Traceback (most recent call last):
...
mm = mmap.mmap(fid.fileno(), bytes, access=acc, offset=start)
mmap.error: [Errno 12] Cannot allocate memory

For me this is quite enough


对我的问题的不同态度的概述

在形状为 [500,30,3]

的数组上测试

1) 简单加载:无缓存

array = np.load(path)
[process rows]

Slowest but most memory efficient

cache_limit = 0 (Arrays in dictionary)

2) 硬缓存 - 将数组加载到字典中

cache_raw = {i: np.load(i) for i in os.listdir('array_folder')}
...
temporary_array = cache_raw[some_array]
[process rows with temporary_array]

Ultra fast but very memory inefficient

cache_limit ~ 1000, (RPI zero) (Arrays in dictionary)

3) Memmap 缓存

cache_memmap = {i: np.load(i,mmap_mode='r') for i in os.listdir('array_folder')}
...
memmap_array = cache_memmap[some_array]
[process rows with memmap_array]

reasonable speed, memory effiecient

cache_limit ~ 8000 (RPI zero) (Arrays in dictionary)


结果

加载第一行的计时结果,针对所有态度进行 20 次随机访问:

Memmap
0.00122714042664
0.00237703323364
0.00152182579041
0.000735998153687
0.000724077224731
0.000736951828003
0.000741004943848
0.000698089599609
0.000723123550415
0.000734090805054
0.000775814056396
0.00343084335327
0.000797033309937
0.000717878341675
0.000727891921997
0.000733852386475
0.000690937042236
0.00178194046021
0.000714063644409
0.000691175460815
Hard cache
0.000302076339722
0.000305891036987
0.000910043716431
0.000320911407471
0.000298976898193
0.000309944152832
0.000294923782349
0.000304937362671
0.000298023223877
0.00031590461731
0.000324010848999
0.000273942947388
0.000274181365967
0.000286817550659
0.000277042388916
0.000297784805298
0.000288009643555
0.000318050384521
0.00031304359436
0.000298023223877
Without cache
0.0350978374481
0.0103611946106
0.0172200202942
0.0349309444427
0.0177171230316
0.00722813606262
0.0286860466003
0.0435371398926
0.0261130332947
0.0302798748016
0.0361919403076
0.0286440849304
0.0175659656525
0.035896062851
0.0307757854462
0.0364079475403
0.0258250236511
0.00768494606018
0.025671005249
0.0261180400848

编辑: 附加计算:

100 次唯一访问的平均时间。每种态度 5 次

Memmap
0.000535547733307 # very good speed
0.000488042831421
0.000483453273773
0.000485241413116
0.00049720287323
Hard cache
0.000133073329926 # 4x faster than memmap
0.000132908821106
0.000131068229675
0.000130603313446
0.000126478672028
Without cache
0.0996991252899 # very slow
0.0946901941299
0.0264434242249 # Interesting to note here, something I suspected
0.0239776492119 # np.load has cache in itself
0.0208633708954 # If you load particular numpy array more times in the program,
#it will load faster. Kind of integrated cache
# From my own experience, it is very unreliable and cannot be counted with.