Запись данных в длительной симуляции Python - PullRequest
0 голосов
/ 30 января 2019

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

existing_data = np.load("existing_record.npy")
updated = np.dstack((existing_data,new_array[...,None]))
np.save("existing_record.npy",updated)

Это создало большое узкое место в производительности, и симуляция выполняется на половине скорости с помощью этого метода.Я рассмотрел добавление в список симуляций массивов и запись их в конце симуляции, но это, очевидно, может привести к исчерпанию памяти или потере данных при сбое и т. Д. Существуют ли стандартные типы решений для такого рода проблем?

Ответы [ 2 ]

0 голосов
/ 31 января 2019

Я думаю, что одним из решений является использование файла с отображенной памятью через numpy.memmap .Код можно найти ниже.Документация содержит важную информацию для понимания кода.

import numpy as np
from os.path import getsize

from time import time

filename = "data.bin"

# Datatype used for memmap
dtype = np.int32

# Create memmap for the first time (w+). Arbitrary shape. Probably good to try and guess the correct size.
mm = np.memmap(filename, dtype=dtype, mode='w+', shape=(1, ))
print("File has {} bytes".format(getsize(filename)))


N = 20
num_data_per_loop = 10**7

# Main loop to append data
for i in range(N):

    # will extend the file because mode='r+'
    starttime = time()
    mm = np.memmap(filename,
                   dtype=dtype,
                   mode='r+',
                   offset=np.dtype(dtype).itemsize*num_data_per_loop*i,
                   shape=(num_data_per_loop, ))
    mm[:] = np.arange(start=num_data_per_loop*i, stop=num_data_per_loop*(i+1))
    mm.flush()
    endtime = time()
    print("{:3d}/{:3d} ({:6.4f} sec): File has {} bytes".format(i, N, endtime-starttime, getsize(filename)))

A = np.array(np.memmap(filename, dtype=dtype, mode='r'))
if np.array_equal(A, np.arange(num_data_per_loop*N, dtype=dtype)):
    print("Correct")

Вывод, который я получаю:

File has 4 bytes
  0/ 20 (0.2167 sec): File has 40000000 bytes
  1/ 20 (0.2200 sec): File has 80000000 bytes
  2/ 20 (0.2131 sec): File has 120000000 bytes
  3/ 20 (0.2180 sec): File has 160000000 bytes
  4/ 20 (0.2215 sec): File has 200000000 bytes
  5/ 20 (0.2141 sec): File has 240000000 bytes
  6/ 20 (0.2187 sec): File has 280000000 bytes
  7/ 20 (0.2138 sec): File has 320000000 bytes
  8/ 20 (0.2137 sec): File has 360000000 bytes
  9/ 20 (0.2227 sec): File has 400000000 bytes
 10/ 20 (0.2168 sec): File has 440000000 bytes
 11/ 20 (0.2141 sec): File has 480000000 bytes
 12/ 20 (0.2150 sec): File has 520000000 bytes
 13/ 20 (0.2144 sec): File has 560000000 bytes
 14/ 20 (0.2190 sec): File has 600000000 bytes
 15/ 20 (0.2186 sec): File has 640000000 bytes
 16/ 20 (0.2210 sec): File has 680000000 bytes
 17/ 20 (0.2146 sec): File has 720000000 bytes
 18/ 20 (0.2178 sec): File has 760000000 bytes
 19/ 20 (0.2182 sec): File has 800000000 bytes
Correct

Время примерно одинаковое на итерациях из-за смещений, используемых для memmap.Кроме того, объем необходимой оперативной памяти (не считая загрузки всего memmap для проверки в конце) постоянен.

Надеюсь, это решит ваши проблемы с производительностью

С уважением

Лукас

Редактировать 1: Кажется, плакат решил свой собственный вопрос.Я оставляю этот ответ в качестве альтернативы.

0 голосов
/ 31 января 2019

Я нашел хорошее рабочее решение с использованием библиотеки h5py.Производительность гораздо лучше, поскольку нет данных для чтения, и я сократил количество операций добавления массива nump.Краткий пример:

with h5py.File("logfile_name", "a") as f:
  ds = f.create_dataset("weights", shape=(3,2,100000), maxshape=(3, 2, None))
  ds[:,:,cycle_num] = weight_matrix

Я не уверен, означает ли разделение по пустым стилям, что матрица копируется, но есть функция write_direct(source, source_sel=None, dest_sel=None), чтобы избежать этого, что может быть полезно для больших матриц.

...