Быстрые варианты сохранения и загрузки для массива 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.