Быстрое преобразование большого (2,1 ГБ +) двоичного файла (широта / долгота / высота в ECEF) - PullRequest
1 голос
/ 20 октября 2011

Сейчас я пытаюсь преобразовать большое количество двоичных файлов точек в формате широты и долготы в текстовый декартовый формат ECEF (x, y, z). Проблема сейчас в том, что процесс очень, очень, очень медленный.

У меня есть более 100 гигабайт этого материала для прохождения, и может поступать больше данных. Я хотел бы сделать этот фрагмент кода максимально быстрым.

Прямо сейчас мой код выглядит примерно так:

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

Мне было интересно, будет ли быстрее читать вещи кусками ~ 4 МБ одним потоком, помещать их в ограниченный буфер, иметь другой поток для преобразования в строковый формат и иметь последний поток, записывающий строку обратно в файл на другом жестком диске. Я мог бы прыгать пистолет, хотя ...

Я сейчас использую python для тестирования, но я бы не стал отказываться от переключения, если бы мог быстрее работать с этими файлами.

Любые предложения будут великолепны. Спасибо

EDIT:

Я снова профилировал код с помощью cProfile и на этот раз разделил формат строки и io. Похоже, что меня на самом деле убивает формат строки ... Вот отчет профилировщика

         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 ]

2 голосов
/ 20 октября 2011

Чтобы более точно атаковать проблему, я предлагаю измерить операцию чтения файла, сделав «convertXYZ» неактивной функцией и синхронизировав результат. И измеряя функцию преобразования, изменяя «чтение», чтобы всегда возвращать простую точку, но вызывая преобразование и выводя то же число раз, как если бы вы действительно читали файл. (И, возможно, еще один прогон, в котором окончательный вывод после преобразования будет невозможен.) В зависимости от того, куда идет время, может иметь гораздо больше смысла атаковать одну или другую.

Возможно, вам удастся заставить локальную ОС выполнить некоторое чередование за вас, записав вывод в стандартный вывод Python и заставив оболочку выполнить фактический ввод-вывод файла. И точно так же путем потоковой передачи файла в стандартный ввод (например, cat oldformat | python conversion.py > outputfile)

В каком хранилище находятся входные и выходные файлы? Характеристики памяти могут иметь гораздо большее отношение к производительности, чем код Python.

Обновление: учитывая, что вывод самый медленный, а ваше хранилище довольно медленное и распределяется между операциями чтения и записи, попробуйте добавить некоторую буферизацию. Начиная с документа Python вы можете добавить некоторую буферизацию, добавив третий аргумент к вызову os.open. Попробуйте что-нибудь довольно большое, например 128 * 1024?

0 голосов
/ 20 октября 2011

2,1 ГБ данных должно занять от 21 (при 100 МБ / с) до 70 (при 30 МБ / с) секунд, чтобы их можно было прочитать. Затем вы форматируете это и записываете данные, которые, возможно, в пять раз больше. Это означает, что в общей сложности 13 ГБ для чтения и записи требуют 130-420 секунд.

Ваша выборка показывает, что чтение занимает 24 секунды. Письмо должно поэтому потребовать приблизительно две минуты. Время чтения и записи можно улучшить, например, с помощью SSD.

Когда я конвертирую файлы (используя программы, которые пишу на C), я предполагаю, что для конвертации должно потребоваться не больше времени, чем для чтения самих данных, обычно гораздо меньше. Перекрытые операции чтения и записи также могут сократить время ввода-вывода. Я пишу свои собственные процедуры форматирования, так как printf обычно слишком медленный.

Сколько стоит 24 секунды? На современном процессоре не менее 40 миллиардов инструкций. Это означает, что за это время вы можете обработать каждый байт данных по крайней мере с 19 инструкциями. Легко выполнимо для программы на C, но не для интерпретируемого языка (Python, Java, C #, VB).

Остаток 525 секунд обработки (549-24) указывает на то, что Python тратит не менее 875 миллиардов инструкций на обработку или 415 инструкций на байт прочитанных данных. Получается соотношение 22 к 1: нередкое соотношение между интерпретируемым и скомпилированным языками. Правильно составленная программа на С должна содержать не более десяти инструкций на байт или менее.

0 голосов
/ 20 октября 2011

Учитывая, что formatString - самая медленная операция, попробуйте это:

def formatString(data_list):
    return " ".join((str(data_list[1]), str(data_list[2]), str(data_list[3])))
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...