首页 > 解决方案 > 将压缩数组完全从磁盘加载到内存中的智能方法是什么 - 也(同样)压缩?

问题描述

我正在尝试存储在磁盘上的 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-arrays 也可以驻留在内存中 - 压缩,就好像它们在磁盘上一样。所以我想为什么不尝试将整个东西加载到具有 32 GB 内存的机器上的 RAM 中。压缩后,数据集将需要大约50% 的 RAM。未压缩时,它需要的 RAM 大约是可用 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,可能会进一步更改。


GitHub 上的相关问题

标签: pythonpython-3.xzarr

解决方案


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

  1. 用于LRUStoreCache在内存中缓存(一些)压缩数据。
  2. 将您的基础商店强制转换为 adict并将其用作您的商店。

如果您只想在内存中使用一些经常使用的数据,则第一个选项可能是合适的。当然,您可以配置多少加载到内存中。所以这可能是整个数组。这只会发生在按需数据的情况下,这可能对您有用。

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


推荐阅读