大型二进制文件(经纬度/高度转ECEF)的快速转换(2.1GB+)
现在,我正在尝试把大量的二进制文件,这些文件里的数据是经纬度和高度的格式,转换成基于文本的ECEF笛卡尔坐标格式(x, y, z)。目前的问题是,这个过程非常非常慢。
我有超过100GB的数据需要处理,而且可能还会有更多的数据进来。我希望能把这段代码尽可能地加快。
现在我的代码大致是这样的:
import mmap
import sys
import struct
import time
pointSize = 41
def getArguments():
if len(sys.argv) != 2:
print """Not enough arguments.
example:
python tllargbin_reader.py input_filename.tllargbin output_filename
"""
return None
else:
return sys.argv
print getArguments()
def read_tllargbin(filename, outputCallback):
f = open(filename, "r+")
map = mmap.mmap(f.fileno(),0)
t = time.clock()
if (map.size() % pointSize) != 0:
print "File size not aligned."
#return
for i in xrange(0,map.size(),pointSize):
data_list = struct.unpack('=4d9B',map[i:i+pointSize])
writeStr = formatString(data_list)
if i % (41*1000) == 0:
print "%d/%d points processed" % (i,map.size())
print "Time elapsed: %f" % (time.clock() - t)
map.close()
def generate_write_xyz(filename):
f = open(filename, 'w', 128*1024)
def write_xyz(writeStr):
f.write(writeStr)
return write_xyz
def formatString(data_list):
return "%f %f %f" % (data_list[1], data_list[2],data_list[3])
args = getArguments()
if args != None:
read_tllargbin(args[1],generate_write_xyz("out.xyz"))
convertXYZ()基本上就是这里的转换公式:http://en.wikipedia.org/wiki/Geodetic_system
我在想,如果用一个线程每次读取大约4MB的数据,把它们放进一个有限的缓冲区,然后用另一个线程进行字符串格式的转换,最后再用一个线程把字符串写回到不同硬盘上的文件,这样会不会更快。不过我可能有点急于求成……
我现在正在用Python进行测试,但如果能更快地处理这些文件,我也不介意换其他语言。
任何建议都很好。谢谢!
编辑:
我又用cProfile对代码进行了分析,这次把字符串格式和输入输出分开了。结果发现,实际上是字符串格式的处理拖慢了速度……这是分析报告:
20010155 function calls in 548.993 CPU seconds
Ordered by: standard name
ncalls tottime percall cumtime percall filename:lineno(function)
1 0.000 0.000 548.993 548.993 <string>:1(<module>)
1 0.016 0.016 548.991 548.991 tllargbin_reader.py:1(<module>)
1 24.018 24.018 548.955 548.955 tllargbin_reader.py:20(read_tllargbin)
1 0.000 0.000 0.020 0.020 tllargbin_reader.py:36(generate_write_xyz)
10000068 517.233 0.000 517.233 0.000 tllargbin_reader.py:42(formatString)
2 0.000 0.000 0.000 0.000 tllargbin_reader.py:8(getArguments)
10000068 6.684 0.000 6.684 0.000 {_struct.unpack}
1 0.002 0.002 548.993 548.993 {execfile}
2 0.000 0.000 0.000 0.000 {len}
1 0.065 0.065 0.065 0.065 {method 'close' of 'mmap.mmap' objects}
1 0.000 0.000 0.000 0.000 {method 'disable' of '_lsprof.Profiler' objects}
1 0.000 0.000 0.000 0.000 {method 'fileno' of 'file' objects}
10003 0.955 0.000 0.955 0.000 {method 'size' of 'mmap.mmap' objects}
2 0.020 0.010 0.020 0.010 {open}
2 0.000 0.000 0.000 0.000 {time.clock}
有没有更快的方法来格式化字符串呢?
3 个回答
考虑到 formatString
是最慢的操作,可以试试这个:
def formatString(data_list):
return " ".join((str(data_list[1]), str(data_list[2]), str(data_list[3])))
读取2.1 GB的数据大约需要21秒(如果速度是100 MB/s)到70秒(如果速度是30 MB/s)。然后你还要把这些数据格式化并写入,可能会产生五倍大小的数据。这就意味着总共需要处理13 GB的数据,读取和写入的时间大约在130到420秒之间。
你的测试显示读取数据需要24秒。因此,写入数据大约需要两分钟。使用固态硬盘(SSD)可以提高读取和写入的速度。
当我转换文件时(使用我自己写的C语言程序),我认为转换的时间不应该超过读取数据的时间,通常可以更快。重叠的读取和写入操作也可以减少输入输出的时间。我自己编写格式化的代码,因为printf通常太慢了。
24秒到底有多长呢?在现代CPU上,这至少相当于处理400亿条指令。这意味着在这段时间内,你可以用至少19条指令处理每一个字节的数据。对于C语言程序来说,这很容易做到,但对于解释型语言(比如Python、Java、C#、VB)来说就不那么简单了。
你提到的525秒的处理时间(549-24)表明,Python在处理时至少消耗了8750亿条指令,也就是每读取一个字节的数据需要415条指令。这是一个常见的比例,解释型语言和编译型语言之间的效率差距。一个设计良好的C语言程序应该能做到每个字节大约只需要十条指令或更少。
为了更准确地解决这个问题,我建议你先测量一下文件读取的操作。可以把'convertXYZ'这个函数变成一个什么都不做的函数,然后看看执行的时间。接着,测量转换函数的时间,把'read'改成总是返回一个简单的点,但调用转换和输出的次数要和真正读取文件时一样。(可能还要再做一次测试,把最后转换后的输出也变成什么都不做。)根据时间的消耗情况,可能会更有意义去解决其中一个问题。
你可以尝试让本地操作系统帮你做一些交替处理,比如把输出写到Python的标准输出,然后让命令行来处理实际的文件输入输出。类似地,你也可以通过将文件流入标准输入来实现(例如,cat oldformat | python conversion.py > outputfile
)。
输入和输出文件存储在哪里?存储的特性可能对性能影响更大,而不是Python代码本身。
更新:考虑到输出是最慢的,而且你的存储速度也很慢,并且在读写之间是共享的,试着增加一些缓冲区。从Python文档中你应该能找到方法,通过在os.open
调用中添加第三个参数来增加缓冲。可以试试比较大的值,比如128*1024?