Быстрые варианты сохранения и загрузки для массива numpy

У меня есть script, который генерирует двумерный numpy array с dtype=float и имеет порядок порядка (1e3, 1e6). Сейчас я использую np.save и np.load для выполнения операций ввода-вывода с массивами. Однако для каждого массива эти функции занимают несколько секунд. Существуют ли более быстрые методы для сохранения и загрузки всех массивов (т.е. Без предположений об их содержании и их сокращения)? Я открыт для преобразования array в другой тип до сохранения до тех пор, пока данные сохраняются точно.

Ответы

Ответ 1

Для действительно больших массивов, я слышал о нескольких решениях, и в основном они ленивы в I/O:

  • NumPy.memmap, отображает большие массивы в двоичную форму
    • Плюсы:
      • Никакая зависимость, кроме Numpy
      • Прозрачная замена ndarray (любой класс, принимающий ndarray, принимает memmap)
    • Минусы:
      • Куски вашего массива ограничены 2.5G
      • Все еще ограничено пропускной способностью Numpy
  • Использовать привязки Python для HDF5, формат файлов с поддержкой больших данных, например PyTables или h5py

    • Плюсы:
      • Формат поддерживает сжатие, индексирование и другие супер-приятные функции.
      • По всей видимости, максимальный формат файла PetaByte
    • Минусы:
      • Кривая обучения для иерархического формата?
      • Необходимо определить, каковы ваши потребности в производительности (см. ниже)
  • система травления питона (из гонки, упомянутой для Pythonity, а не скорости)

    • Плюсы:
      • Это Pythonic! (ха-ха)
      • Поддерживает всевозможные объекты
    • Минусы:
      • Вероятно, медленнее, чем другие (потому что нацелены на любые объекты, а не на массивы)

Numpy.memmap

Из документов NumPy.memmap:

Создайте карту памяти для массива, хранящегося в двоичном файле на диске.

Файлы с отображением памяти используются для доступа к небольшим сегментам больших файлов на диске, не считывая весь файл в память

Объект memmap можно использовать везде, где принимается ndarray. Для любого memmap fp, isinstance(fp, numpy.ndarray) возвращает значение True.


массивы HDF5

Из h5py doc

Позволяет хранить огромное количество числовых данных и легко манипулировать этими данными из NumPy. Например, вы можете нарезать многотабайтные наборы данных, хранящиеся на диске, как если бы они были реальными массивами NumPy. Тысячи наборов данных могут быть сохранены в одном файле, классифицированы и помечены, как вы хотите.

Формат поддерживает сжатие данных по-разному (большее количество бит загружается для одного и того же чтения ввода-вывода), но это означает, что данные становятся менее легкими для запроса по отдельности, но в вашем случае (чисто нагрузочные/демпинговые массивы) он может быть эффективным

Ответ 2

Вот сравнение с PyTables.

Я не могу добраться до (int(1e3), int(1e6) из-за ограничений памяти. Поэтому я использовал меньший массив:

data = np.random.random((int(1e3), int(1e5)))

NumPy save:

%timeit np.save('array.npy', data)
1 loops, best of 3: 4.26 s per loop

NumPy load:

%timeit data2 = np.load('array.npy')
1 loops, best of 3: 3.43 s per loop

PyTables:

%%timeit
with tables.open_file('array.tbl', 'w') as h5_file:
    h5_file.create_array('/', 'data', data)

1 loops, best of 3: 4.16 s per loop

Чтение PyTables:

 %%timeit
 with tables.open_file('array.tbl', 'r') as h5_file:
      data2 = h5_file.root.data.read()

 1 loops, best of 3: 3.51 s per loop

Цифры очень похожи. Так что никакой реальной выгоды от PyTables здесь нет. Но мы очень близки к максимальной скорости записи и чтения моего SSD.

Запись:

Maximum write speed: 241.6 MB/s
PyTables write speed: 183.4 MB/s

Чтение:

Maximum read speed: 250.2
PyTables read speed: 217.4

Сжатие на самом деле не помогает из-за случайности данных:

%%timeit
FILTERS = tables.Filters(complib='blosc', complevel=5)
with tables.open_file('array.tbl', mode='w', filters=FILTERS) as h5_file:
    h5_file.create_carray('/', 'data', obj=data)
1 loops, best of 3: 4.08 s per loop

Чтение сжатых данных становится немного медленнее:

%%timeit
with tables.open_file('array.tbl', 'r') as h5_file:
    data2 = h5_file.root.data.read()

1 loops, best of 3: 4.01 s per loop

Для обычных данных это отличается:

 reg_data = np.ones((int(1e3), int(1e5)))

Запись значительно быстрее:

%%timeit
FILTERS = tables.Filters(complib='blosc', complevel=5)
with tables.open_file('array.tbl', mode='w', filters=FILTERS) as h5_file:
    h5_file.create_carray('/', 'reg_data', obj=reg_data)

1 петли, лучше всего 3: 849 мс за цикл

То же самое верно для чтения:

%%timeit
with tables.open_file('array.tbl', 'r') as h5_file:
    reg_data2 = h5_file.root.reg_data.read()

1 loops, best of 3: 1.7 s per loop

Заключение. Чем правильнее ваши данные, тем быстрее он будет работать с PyTables.

Ответ 3

Согласно моему опыту, np.save() & np.load() является самым быстрым решением при передаче данных между жестким диском и памятью до сих пор. Я сильно полагался на свою загрузку данных в базе данных и системе HDFS, прежде чем понял этот вывод. Мои тесты показывают, что: Пропускная способность загрузки данных в базу данных (с жесткого диска в память) может составлять около 50 Мбит/с (байт/сек), но пропускная способность np.load() практически равна максимальной пропускной способности моего жесткого диска: 2 Гбит/с (байт/сек). Обе тестовые среды используют простейшую структуру данных.

И я не считаю проблемой использовать несколько секунд для загрузки массива с формой: (1e3, 1e6). Например. Форма массива (1000, 1000000), тип данных - float128, тогда чистый размер данных (128/8) * 1000 * 1 000 000 = 16 000 000 000 = 16 ГБ. и если это займет 4 секунды, Тогда ваша пропускная способность загрузки данных составляет 16 ГБ /4 секунды = 4 ГБ/с. Максимальная пропускная способность SATA3 составляет 600 МБ/с = 0,6 ГБ/с, пропускная способность при загрузке данных уже в 6 раз выше, производительность при загрузке данных практически может конкурировать с максимальной пропускной способностью DDR, что еще вы хотите?

Итак, мой окончательный вывод:

Не используйте Python Pickle, не используйте любую базу данных, не используйте систему больших данных для хранения ваших данных на жестком диске, если вы можете использовать np.save() и np.load(). Эти две функции являются самым быстрым решением для передачи данных между жестким диском и памятью.

Я также протестировал HDF5 и обнаружил, что он намного медленнее, чем np.load() и np.save(), поэтому используйте np.save() & np.load(), если вы ' В вашей платформе достаточно памяти DDR.