Что такое интеллектуальный способ полной загрузки сжатого массива с диска в память - также (по сути) сжатого? - PullRequest
0 голосов
/ 31 марта 2019

Я экспериментирую с трехмерным массивом 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 ГБ памяти. Сжатый , для набора данных потребуется приблизительно 50% ОЗУ .В несжатом виде потребовалось бы примерно в 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[:, :, :] попытается создать несжатый полноразмерный массив с нулевыми значениями, который, очевидно, потерпит неудачу.

Вторая попытка, которая работает, но мучительно медленная:

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

Ответы [ 2 ]

0 голосов
/ 17 апреля 2019

Сегодня можно решить эту проблему несколькими способами.

  1. Используйте LRUStoreCache для кэширования (некоторых) сжатых данных в памяти.
  2. Приведите ваше базовое хранилище в dict и используйте его в качестве хранилища.

Первый вариант может быть уместным, если вам нужны только некоторые часто используемые данные в памяти.Конечно, сколько вы загружаете в память, это то, что вы можете настроить.Так что это может быть весь массив.Это произойдет только с данными по запросу, что может быть полезно для вас.

Второй вариант просто создает новую копию массива в памяти путем извлечения всех сжатых данных с диска.Единственным недостатком является то, что если вы намереваетесь записать обратно на диск, это будет то, что вам нужно сделать вручную, но это не слишком сложно.Метод update очень удобен для облегчения копирования данных между различными хранилищами.

0 голосов
/ 12 апреля 2019

Вы можете предположительно попробовать с fsspec.implementations.memory.MemoryFileSystem, который имеет метод .make_mapper(), с помощью которого вы можете сделать вид объекта, ожидаемый zarr.

Однако, это действительно просто путь: io.BytesIO, который вы можете сделать сами, если хотите.

...