将压缩的阵列从磁盘完全加载到内存中(也(压缩地)压缩)的智能方法是什么?

时间:2019-03-31 17:24:32

标签: python python-3.x zarr

我正在试验存储在磁盘上的3维zarr数组:

Name: /data
Type: zarr.core.Array
Data type: int16
Shape: (102174, 1100, 900)
Chunk shape: (12, 220, 180)
Order: C
Read-only: True
Compressor: Blosc(cname='zstd', clevel=3, shuffle=BITSHUFFLE, blocksize=0)
Store type: zarr.storage.DirectoryStore
No. bytes: 202304520000 (188.4G)
No. bytes stored: 12224487305 (11.4G)
Storage ratio: 16.5
Chunks initialized: 212875/212875

据我了解,zarr数组也可以驻留在内存中-压缩后,就好像它们在磁盘上一样。因此,我想为什么不尝试在具有32 GB内存的计算机上将所有内容加载到RAM 中。 压缩,数据集大约需要 50%的RAM 。未经压缩,将需要比可用内存多6倍的内存。

准备工作:

import os
import zarr
from numcodecs import Blosc
import tqdm
zpath = '...' # path to zarr data folder

disk_array = zarr.open(zpath, mode = 'r')['data']

c = Blosc(cname = 'zstd', clevel=3, shuffle = Blosc.BITSHUFFLE)
memory_array = zarr.zeros(
    disk_array.shape, chunks = disk_array.chunks,
    dtype = disk_array.dtype, compressor = c
    )

以下实验几乎立即失败,并显示内存不足错误:

memory_array[:, :, :] = disk_array[:, :, :]

据我了解,disk_array[:, :, :]将尝试创建一个未压缩的全尺寸numpy数组,该数组显然会失败。

第二次尝试,虽然有效,但非常缓慢:

chunk_lines = disk_array.chunks[0]
chunk_number = disk_array.shape[0] // disk_array.chunks[0]
chunk_remain = disk_array.shape[0] % disk_array.chunks[0] # unhandled ...
for chunk in tqdm.trange(chunk_number):
    chunk_slice = slice(chunk * chunk_lines, (chunk + 1) * chunk_lines)
    memory_array[chunk_slice, :, :] = disk_array[chunk_slice, :, :]

在这里,我试图一次读取一定数量的块,并将它们放入我的内存阵列中。 它可以工作,但是比最初将此东西写入磁盘要慢大约6到7倍。编辑:是的,它仍然很慢,但是发生了6到7次磁盘问题。

什么是实现这一目标的明智而快速的方法?我想,除了没有使用正确的方法外,我的数据块可能也太小了-但我不确定。

编辑:形状,块大小和压缩对于磁盘阵列和内存阵列应该是相同的。因此,在上面的示例中应该可以消除解压缩过程。

我找到了zarr.convenience.copy,但它被标记为experimental feature,可能会进一步更改。


Related issue on GitHub

2 个答案:

答案 0 :(得分:0)

可以想象,您可以尝试使用fsspec.implementations.memory.MemoryFileSystem,它具有一种.make_mapper()方法,通过该方法可以创建zarr期望的对象。

但是,这实际上只是path:io.BytesIO的命令,如果需要,您可以自己创建。

答案 1 :(得分:0)

今天有几种方法可以解决此问题。

  1. 使用LRUStoreCache将压缩后的数据缓存在内存中。
  2. 将您的基础商店强制为dict并将其用作您的商店。

如果只希望在内存中使用一些经常使用的数据,则第一个选项可能是合适的。当然,可以配置多少内存。因此,这可能是整个数组。这只会发生在按需数据上,这可能对您有用。

第二个选项只是通过从磁盘中提取所有压缩数据来创建阵列的新内存副本。缺点之一是,如果您打算写回磁盘,则需要手动执行此操作,但这并不是太困难。 update method非常便于在不同商店之间进行数据复制。

相关问题