我有3D数据量(x,y,z),我想保存2D切片(xy,yz,xz平面)并保存以备将来使用。
我尝试这样做的方法是让一个函数(slice_data
)来获取切片,而另一个函数(save_slices
)来调用slice_data
,然后使用{{1} }保存切片。
如果我不保存切片,则无论我是否提取xy,yz,xz平面,拍摄切片的时间都差不多。 但是,如果我保存切片,则保存切片的时间取决于切片的方向,并且对于xy,yz,xz平面而言是不同的
那是为什么?使用我的全部数据,这种差异从几分钟到几小时...
我在下面编写了一个伪代码,类似于我在完整数据集中用来演示该问题的代码。两者之间的时差如下所示:
不保存,仅切片
numpy.save
切片和保存:
mean time x-slice: 0.00011536836624145507 sec
mean time y-slice: 0.00011417627334594726 sec
mean time z-slice: 0.00011371374130249023 sec
代码:
mean time x-slice: 0.04629791975021362 sec
mean time y-slice: 0.06096100091934204 sec
mean time z-slice: 0.08996494293212891 sec
答案 0 :(得分:1)
原因是默认情况下Numpy以行主要顺序存储数据。如果您更改
data = np.zeros((512,512,256))
到
# order F means column major
data = np.zeros((512,512,256), order='F')
您将看到保存X切片将花费最长的时间。
如果要保存XY平面的多个切片(当更改为Z坐标时),则可以通过转置数组并将其复制到force a new memory layout.来获得更好的性能,这将确保内存布局适合您的访问模式,从而可以更快地读取(和保存)。下面有更详细的说明。
让我们以以下矩阵为例(来自Numpy glossary):
m = [[1, 2, 3],
[4, 5, 6]]
如果这在内存中以行主顺序(在numpy语言中为C顺序)表示,则其布局如下:
[1, 2, 3, 4, 5, 6]
如果矩阵在内存中以列主顺序表示(对于Fortran顺序,则为F),则其布局如下:
[1, 4, 2, 5, 3, 6]
现在,如果您使用m[:,2]
索引到此数组中,则会得到[3, 6]
,而如果使用m[1,:]
,则会得到[4, 5, 6]
。如果回头看一下内存布局,您会发现[3, 6]
的值在列主要表示形式中是连续的,而[4, 5, 6]
在行的主要表示形式中是连续的。
从数组中读取大量元素时(如保存一个元素时的情况),连续读取这些值的性能要好得多,因为这样可以利用1-2的CPU缓存faster个数量级,而不是从内存中读取。
答案 1 :(得分:1)
仅roix
数组是c_contiguous。因此,从内存到CPU的总线传输要比非连续数据传输快(由于总线将数据按块移动并缓存)
通过将其设置为C连续np.save(file, np.asarray(a, order='C'))
您应该使用timeit来计时表演而不是自定义方法。
我已经为您展示了这些示例:
在一个单元格中,我们得到了:
import os
import numpy as np
import time
import matplotlib.pyplot as plt
# take a slice of the data
def slice_data(roi):
dic = {}
data = np.zeros((512,512,256))
dic['data'] = np.squeeze( data[roi[0]:roi[1]+1, roi[2]:roi[3]+1, roi[4]:roi[5]+1] )
return dic
# save slices if the data
def save_slices(roi, save=False):
var = 'data'
for i in range(0,6):
# iterate to simulate a time series of data
a = slice_data(roi)[var]
var_dir = 'save_test/'
if not os.path.exists(var_dir): os.makedirs(var_dir)
file = var_dir + '{0:04d}{1}'.format(i,'.npy')
if save is True:
np.save(file, a)
## define slices
roix=[256, 256, 0, 512, 0, 256] # yz plane slice
roiy=[0, 512, 256, 256, 0, 256] # xz plane slice
roiz=[0, 512, 0, 512, 128, 128] # xy plane slice
在其他人中:
%%timeit -n 100
save_slices(roix) # 19.8 ms ± 285 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
%%timeit -n 100
save_slices(roiy) # 20.5 ms ± 948 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
%%timeit -n 100
save_slices(roiz) # 20 ms ± 345 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
保存后
%%timeit -n 10 -r 3
save_slices(roix, True) # 32.7 ms ± 2.31 ms per loop (mean ± std. dev. of 3 runs, 10 loops each)
%%timeit -n 10 -r 3
save_slices(roiy, True) # 101 ms ± 2.61 ms per loop (mean ± std. dev. of 3 runs, 10 loops each)
%%timeit -n 10 -r 3
save_slices(roix, True) # 1.9 s ± 21.1 ms per loop (mean ± std. dev. of 3 runs, 10 loops each)
因此,正如您已经注意到的那样,在不保存的情况下,性能是相同的!
让我们进入np.save()
方法
np.save
负责io流,并调用write_array方法。对于C_contigous数组,这确实非常快。 (快速访问内存)
让我们验证这个假设:
np.squeeze( np.zeros((512,512,256))[roix[0]:roix[1]+1, roix[2]:roix[3]+1, roix[4]:roix[5]+1] ).flags.c_contiguous # returns True
np.squeeze( np.zeros((512,512,256))[roiy[0]:roiy[1]+1, roiy[2]:roiy[3]+1, roiy[4]:roiy[5]+1] ).flags.c_contiguous # returns False
np.squeeze( np.zeros((512,512,256))[roiz[0]:roiz[1]+1, roiz[2]:roiz[3]+1, roiz[4]:roiz[5]+1] ).flags.c_contiguous # returns False
因此,这可以解释roix
和roiy
/ roiz
之间的区别。
roiy
和roiz
之间差异的可能解释。数据传输会降低程序速度在那之后,我只能作个假设,roiz
似乎比roiy
更加分散。 write_array
方法会花费很多时间。
我现在无法自己对此进行测试,但是可以使用linux中的perf
命令来验证这一部分。 (要查看使用的时间总线数量,例如高速缓存未命中的数量)。
如果我必须做一个大胆的猜测,我会说由于数据不连续而导致高速缓存丢失率很高。因此,将数据从RAM传输到CPU确实会减慢该过程。
我还没有尝试过,但是有一个很好的问题,提供了一些有用的答案:best way to preserve numpy arrays on disk