我有一个整数存储为二进制的文件,我试图在特定位置提取值。它是一个大的序列化整数数组,我需要特定索引的值。我创建了以下代码,但与之前创建的F#版本相比,它的速度非常慢。
import os, struct
def read_values(filename, indices):
# indices are sorted and unique
values = []
with open(filename, 'rb') as f:
for index in indices:
f.seek(index*4L, os.SEEK_SET)
b = f.read(4)
v = struct.unpack("@i", b)[0]
values.append(v)
return values
为了比较,这里是F#版本:
open System
open System.IO
let readValue (reader:BinaryReader) cellIndex =
// set stream to correct location
reader.BaseStream.Position <- cellIndex*4L
match reader.ReadInt32() with
| Int32.MinValue -> None
| v -> Some(v)
let readValues fileName indices =
use reader = new BinaryReader(File.Open(fileName, FileMode.Open, FileAccess.Read, FileShare.Read))
// Use list or array to force creation of values (otherwise reader gets disposed before the values are read)
let values = List.map (readValue reader) (List.ofSeq indices)
values
有关如何提高python版本性能的任何提示,例如:使用numpy?
更新
Hdf5工作得非常好(我的测试文件从5秒到0.8秒):
import tables
def read_values_hdf5(filename, indices):
values = []
with tables.open_file(filename) as f:
dset = f.root.raster
return dset[indices]
更新2
我使用了np.memmap,因为性能类似于hdf5,我已经在生产中使用了numpy。
答案 0 :(得分:4)
根据您的索引文件大小,您可能希望将其完全读入numpy数组。如果文件不大,则完整的顺序读取可能比大量的搜索更快。
搜索操作的一个问题是python在缓冲输入上运行。如果程序是用某种低级语言编写的,那么在无缓冲IO上使用将是一个好主意,因为你只需要几个值。
import numpy as np
# read the complete index into memory
index_array = np.fromfile("my_index", dtype=np.uint32)
# look up the indices you need (indices being a list of indices)
return index_array[indices]
如果您无论如何都会读取几乎所有页面(即您的索引是随机的并且频率为1/1000或更高),这可能会更快。另一方面,如果你有一个大的索引文件,并且你只想选择一些索引,那就不那么快了。
然后,另一种可能性 - 可能是最快的 - 是使用python mmap
模块。然后文件被内存映射,只访问真正需要的页面。
它应该是这样的:
import mmap
with open("my_index", "rb") as f:
memory_map = mmap.mmap(mmap.mmap(f.fileno(), 0)
for i in indices:
# the index at position i:
idx_value = struct.unpack('I', memory_map[4*i:4*i+4])
(注意,我实际上没有测试过那个,所以可能会有输入错误。另外,我不关心endianess,所以请检查它是否正确。)
令人高兴的是,可以使用numpy.memmap
来组合这些。它应该将您的阵列保留在磁盘上,但会给您带来numpyish索引。它应该像以下一样简单:
import numpy as np
index_arr = np.memmap(filename, dtype='uint32', mode='rb')
return index_arr[indices]
我认为这应该是最简单,最快捷的选择。但是,如果&#34;快速&#34;很重要,请测试和分析。
编辑:由于mmap
解决方案似乎越来越流行,我将添加一些关于内存映射文件的文字。
什么是mmap?
内存映射文件不是pythonic独有的东西,因为内存映射是POSIX标准中定义的内容。内存映射是一种使用设备或文件的方式,就好像它们只是内存中的区域一样。
文件内存映射是随机访问固定长度数据文件的一种非常有效的方法。它使用与虚拟内存相同的技术。读写是普通的内存操作。如果它们指向不在物理RAM存储器中的存储器位置(&#34;页面错误&#34;发生),则将所需的文件块(页面)读入存储器。
随机文件访问的延迟主要是由于磁盘的物理旋转(SSD是另一个故事)。平均而言,您需要的挡块旋转半圈;对于典型的HDD,此延迟大约为5 ms加上任何数据处理延迟。与此延迟相比,使用python而不是编译语言引入的开销可以忽略不计。
如果按顺序读取文件,操作系统通常会使用预读缓存来缓冲文件,甚至在您知道需要之前。对于随机访问的大文件,这根本没有用。内存映射提供了一种非常有效的方法,因为所有块都在您需要时准确加载并保留在缓存中以供进一步使用。 (这原则上也可以与fseek
一起发生,因为它可能在幕后使用相同的技术。但是,无法保证,当呼叫在操作系统中漫游时,无论如何都会有一些开销。)
mmap
也可用于编写文件。从某种意义上讲,它非常灵活,可以由多个进程共享单个内存映射文件。在某些情况下,这可能非常有用和高效,mmap
也可用于进程间通信。在这种情况下,通常没有为mmap
指定文件,而是创建了内存映射,后面没有文件。
mmap
尽管有用且易于使用,但并不是很有名。然而,它有一个重要的问题。文件大小必须保持不变。如果它在mmap
期间发生变化,可能会发生奇怪的事情。
答案 1 :(得分:1)
索引列表是否排序?我认为如果对列表进行排序,你可以获得更好的性能,因为你可以减少磁盘搜索次数