Python:在大文件中迭代的最快方法

Python:在大文件中迭代的最快方法,python,binary,numpy,iteration,Python,Binary,Numpy,Iteration,对,我正在遍历一个大的二进制文件 我需要尽量缩短此循环的时间: def NB2(self, ID_LEN): r1=np.fromfile(ReadFile.fid,dTypes.NB_HDR,1) num_receivers=r1[0][0] num_channels=r1[0][1] num_samples=r1[0][5] blockReturn = np.zeros((num_samples,num_receivers,num_channels)

对,我正在遍历一个大的二进制文件

我需要尽量缩短此循环的时间:

def NB2(self, ID_LEN):
    r1=np.fromfile(ReadFile.fid,dTypes.NB_HDR,1)
    num_receivers=r1[0][0]
    num_channels=r1[0][1]
    num_samples=r1[0][5]

    blockReturn = np.zeros((num_samples,num_receivers,num_channels))

    for rec in range(0,num_receivers):
        for chl in range(0,num_channels):
            for smpl in range(0,num_samples):
                r2_iq=np.fromfile(ReadFile.fid,np.int16,2)
                blockReturn[smpl,rec,chl] = np.sqrt(math.fabs(r2_iq[0])*math.fabs(r2_iq[0]) + math.fabs(r2_iq[1])*math.fabs(r2_iq[1]))

    return blockReturn
因此,情况如下: r1是文件的头,dTypes.NB_HDR是我制作的类型:

NB_HDR= np.dtype([('f3',np.uint32),('f4',np.uint32),('f5',np.uint32),('f6',np.int32),('f7',np.int32),('f8',np.uint32)])
这将获得关于即将到来的数据块的所有信息,并很好地将我们放在文件中的正确位置(数据块的开始!)

在该数据块中有: 每个通道4096个样本, 每个接收器4个通道, 9名接收者

所以num_接收器、num_通道、num_采样将始终是相同的(无论如何目前),但正如您所看到的,这是一个相当大的数据量。每个“样本”都是一对int16值,我想找出它们的大小(因此是毕达哥拉斯)

这个NB2代码是为文件中的每个“块”执行的,对于一个12GB的文件(即它们有多大),大约有20900个块,我需要迭代1000个这些文件(因此,总容量为12TB)。任何速度优势,即使是毫秒级,都将受到极大的赞赏

编辑:事实上,了解我在文件中的移动方式可能会有所帮助。我的职能如下:

def navigateTo(self, blockNum, indexNum):
    ReadFile.fid.seek(ReadFile.fileIndex[blockNum][indexNum],0)
    ReadFile.currentBlock = blockNum
    ReadFile.index = indexNum
在运行所有这些代码之前,我扫描文件并在ReadFile.fileIndex上列出索引位置,我使用此函数浏览这些位置,然后“搜索”到绝对位置-这是否有效


欢呼[

这是一个观察,而不是一个解决方案,但是把函数移植到C++中并用python API加载它会在循环优化之前从中获得很多速度增益。

我会尽量使用尽可能少的循环和尽可能多的常量。 任何可以线性方式完成的事情都应该这样做。 如果值不变,请使用常量减少查找等, 因为这会消耗cpu周期

这是从理论的角度来看;-)

如果可能,使用高度优化的库。我不知道您想要实现什么,但我宁愿使用现有的FFT库,也不愿自己编写:>

还有一件事:(可以大开眼界)

这应该有助于你的表现。numpy工作中的两个主要思想。首先,对于内存位置,结果数组维度应该与循环维度的构造方式相匹配。
其次,Numpy很快。我用numpy打败了手工编码的C,因为它使用了LAPack和向量加速。然而,为了获得这种能力,你必须让它一次处理更多的数据。这就是为什么您的采样循环已折叠,以便在一次大的读取中读取接收器和通道的完整采样。然后使用numpy的最高向量幂来计算点积的大小


在震级计算中还有一点需要优化,但是numpy为您回收缓冲区,使其没有您想象的那么重要。我希望这有帮助

最重要的是,您不应该在三重嵌套循环的最低级别进行文件访问,无论您是在C还是Python中执行此操作。您必须一次读取大量数据


因此,为了加快速度,一次读取大量数据,并使用numpy索引处理这些数据(即,将代码矢量化)。这在您的情况下尤其容易,因为您的所有数据都是int32。只需读入大块数据,并将数据重塑为反映(接收器、通道、样本)结构的数组,然后使用适当的索引对毕达哥拉斯进行乘法和加法运算,并使用“sum”命令将结果数组中的项相加

因为您在读取标题后知道块的长度,所以请立即读取整个块。然后重塑数组(非常快,只影响元数据),并使用
np.hypot
ufunc:

blockData = np.fromfile(ReadFile.fid, np.int16, num_receivers*num_channels*num_samples*2)
blockData = blockData.reshape((num_receivers, num_channes, num_samples, 2))
return np.hypot(blockData[:,:,:,0], blockData[:,:,:,1])

在我的机器上,它每块运行11m。

我根本不知道C++(我知道的尴尬)。你想怎么做?我想象三维数组和二进制抽取一起直接到int16,而不需要进行位交换,所有这些低级的肮脏不是很容易吗?我不认为这有你想象的那么糟糕。也就是说,我很难想象您的数据结构,因为我不是Python中最流利的。使用ifstream::seekg()函数,您可以根据需要的字节数按顺序获取数字,并在向量中强制转换+存储。@Duncan Tait:您完全没有理由感到尴尬。如果您有足够的内存同时将所有接收器的通道加载到内存中,这是一个非常棒的解决方案。非常棒,字面上说,谢谢在每个区块10毫秒内完成,这是10倍的改进!非常感谢这个深入的回答-我尝试了这个和下面的一个,速度稍微快一点。
blockData = np.fromfile(ReadFile.fid, np.int16, num_receivers*num_channels*num_samples*2)
blockData = blockData.reshape((num_receivers, num_channes, num_samples, 2))
return np.hypot(blockData[:,:,:,0], blockData[:,:,:,1])