【问题标题】:Recording data in a long running python simulation在长时间运行的 python 模拟中记录数据
【发布时间】:2019-01-30 16:13:39
【问题描述】:

我正在运行一个模拟,我需要在每个循环中记录一些小的 numpy 数组。我目前的解决方案是加载,写入然后保存如下:

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

这造成了很大的性能瓶颈,使用这种方法的模拟运行速度只有一半。我已经考虑将 numpy 数组附加到一个列表中并在模拟结束时将其写入,但这显然会导致内存不足或在崩溃中丢失数据等。这种问题有没有标准类型的解决方案?

【问题讨论】:

  • 我不知道这个的标准解决方案。将它们保存在单独的文件中有什么问题?

标签: python performance numpy storage simulation


【解决方案1】:

我使用 h5py 库找到了一个很好的工作解决方案。性能要好得多,因为没有读取数据,并且我减少了 num 数组追加操作的数量。一个简短的例子:

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

我不确定 numpy 样式切片是否意味着矩阵被复制,但有一个 write_direct(source, source_sel=None, dest_sel=None) 函数可以避免这种情况发生,这对于更大的矩阵可能很有用。

【讨论】:

  • 对此的小提示,因为h5py 使用行主要存储(或“C 风格”),如果一次写入整行应该会更快,因为这将在磁盘上连续.
【解决方案2】:

我认为一种解决方案是通过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 的偏移量,时间在迭代过程中大致恒定。此外,所需的 RAM 量(除了加载整个 memmap 以供最后检查之外)是恒定的。

我希望这能解决您的性能问题

亲切的问候

卢卡斯

编辑 1:看来发帖人已经解决了他自己的问题。我留下这个答案作为替代。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2014-03-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-08-26
    • 2022-01-19
    • 2013-04-03
    • 2012-09-28
    相关资源
    最近更新 更多