我认为您在这里遇到的瓶颈是双重的。
根据您的操作系统和磁盘控制器,对f.read(2) 和f 是一个大文件的调用通常会被有效缓冲——usually。换句话说,操作系统会将磁盘上的一个或两个扇区(磁盘扇区通常为几 KB)读取到内存中,因为这并不比从该文件中读取 2 个字节贵很多。额外的字节被有效地缓存在内存中,为下一次调用读取该文件做好准备。不要依赖这种行为——这可能是你的瓶颈——但我认为这里还有其他问题。
我更关心将单字节转换为对 numpy 的短而单次调用。这些根本没有缓存。您可以将所有短裤保存在一个 Python 整数列表中,并在需要时(如果需要)将整个列表转换为 numpy。您还可以拨打一个电话 struct.unpack_from 来转换缓冲区中的所有内容,而不是一次转换一个短内容。
考虑:
#!/usr/bin/python
import random
import os
import struct
import numpy
import ctypes
def read_wopper(filename,bytes=2,endian='>h'):
buf_size=1024*2
buf=ctypes.create_string_buffer(buf_size)
new_buf=[]
with open(filename,'rb') as f:
while True:
st=f.read(buf_size)
l=len(st)
if l==0:
break
fmt=endian[0]+str(l/bytes)+endian[1]
new_buf+=(struct.unpack_from(fmt,st))
na=numpy.array(new_buf)
return na
fn='bigintfile'
def createmyfile(filename):
bytes=165924350
endian='>h'
f=open(filename,"wb")
count=0
try:
for int in range(0,bytes/2):
# The first 32,767 values are [0,1,2..0x7FFF]
# to allow testing the read values with new_buf[value<0x7FFF]
value=count if count<0x7FFF else random.randint(-32767,32767)
count+=1
f.write(struct.pack(endian,value&0x7FFF))
except IOError:
print "file error"
finally:
f.close()
if not os.path.exists(fn):
print "creating file, don't count this..."
createmyfile(fn)
else:
read_wopper(fn)
print "Done!"
我创建了一个 165,924,350 字节 (158.24 MB) 的随机短裤签名整数文件,相当于 82,962,175 个带符号的 2 字节短裤。使用这个文件,我运行了上面的read_wopper 函数,它运行在:
real 0m15.846s
user 0m12.416s
sys 0m3.426s
如果你不需要短裤是 numpy,这个函数在 6 秒内运行。所有这些都在 OS X、python 2.6.1 64 位、2.93 GHz Core i7、8 GB 内存上。如果将read_wopper 中的buf_size=1024*2 更改为buf_size=2**16,则运行时间为:
real 0m10.810s
user 0m10.156s
sys 0m0.651s
所以我认为你的主要瓶颈是单字节调用解包 - 而不是你从磁盘读取的 2 字节。您可能需要确保您的数据文件没有碎片,如果您使用的是 OS X,您的free disc space(和here)没有碎片。
编辑我发布了完整的代码来创建然后读取整数的二进制文件。在我的 iMac 上,读取随机整数文件的时间始终小于 15 秒。由于一次创作是短暂的,因此创作大约需要 1:23。