2011-02-10 4 views
21

Я профилировал некоторый унаследованный код, который унаследовал с помощью cProfile. Существовала куча изменений, которые я уже сделал, которые помогли (например, с помощью простых расширений C++).Ускорение записи в файлы

В основном этот скрипт экспортирует данные из одной системы в файл фиксированной ширины ASCII. Каждая строка - это запись, и она имеет много значений. Каждая строка имеет 7158 символов и содержит тонну пробелов. Общее количество записей составляет 1,5 миллиона записей. Каждая строка генерируется по одному за раз и занимает некоторое время (5-10 строк в секунду).

Поскольку каждая строка сгенерирована, она записывается на диск как можно проще. Профилирование показывает, что около 19-20% от общего количества времени проведено в file.write(). Для тестового примера 1500 строк, которые составляют 20 секунд. Я бы хотел сократить это число.

Теперь кажется, что следующая победа будет уменьшать количество времени, затрачиваемого на запись на диск. Я хотел бы уменьшить его, если это возможно. Я могу хранить кэш записей в памяти, но я не могу дождаться конца и сбросить все это сразу.

fd = open(data_file, 'w') 
for c, (recordid, values) in enumerate(generatevalues()): 
     row = prep_row(recordid, values) 
     fd.write(row) 
     if c % 117 == 0: 
       if limit > 0 and c >= limit: 
         break 
       sys.stdout.write('\r%s @ %s' % (str(c + 1).rjust(7), datetime.now())) 
       sys.stdout.flush() 

Моя первая мысль состояла в том, чтобы сохранить кэш записей в списке и записать их в виде партий. Будет ли это быстрее? Что-то вроде:

rows = [] 
for c, (recordid, values) in enumerate(generatevalues()): 
     rows.append(prep_row(recordid, values)) 
     if c % 117 == 0: 
      fd.write('\n'.join(rows)) 
      rows = [] 

Моей второй мыслью было бы использовать другую тему, но это заставляет меня хотеть умереть внутри.

+0

В чем заключается узкое место приложения? –

+2

Я думал, что я поняла. Он тратит 20% своего времени на запись на одну строку за раз. – chmullig

+0

Что ж, внесите изменения и профайл? Ожидается, что это будет иметь небольшой эффект, поскольку файловый ввод-вывод, как правило, буферизируется ... или так я предполагаю. – delnan

ответ

19

Группировка записей в группы по 500 действительно значительно ускорила запись. Для этого тестового примера строки ввода индивидуально занимали 21.051 секунд в I/O, в то время как запись в партиях 117 заняла 5,685 секунды, чтобы написать такое же количество строк. Партии из 500 заняли всего 0.266 секунды.

+3

, и теперь вы можете добавить потоки для записи файла для записи asynhronus – ted

+0

Еще лучше - просто выведите на stdout, подключите его к файлу и позвольте ОС делать буферизацию и пакетную запись для вас. :) –

+0

@ LesterCheung, не могли бы вы уточнить? – blindguy

32

На самом деле, ваша проблема не в том, что file.write() занимает 20% вашего времени. Это то, что 80% времени вы не в file.write()!

Запись на диск медленная. Вы ничего не можете с этим поделать. Для записи материала на диск требуется очень много времени. Вы почти ничего не можете сделать, чтобы ускорить его.

Вы хотите, чтобы время ввода-вывода являлось самой большой частью программы, так что ваша скорость ограничена скоростью жесткого диска, а не временем обработки. Идеал для file.write(), чтобы иметь 100% использование!

+0

+1, и обычно я согласен с вами. Однако в этом случае внешний процесс, который я использую для генерации данных, очень медленный, и я хочу как можно больше минимизировать все остальное. Полагаю, я должен поговорить за считанные секунды и сосредоточиться на этом. Будет редактировать, чтобы уточнить количество секунд. – chmullig

+0

Обновлено, чтобы указать, что для записи 1500 записей требуется 20 секунд. Я действительно забочусь об этом числе, я просто использовал проценты, чтобы доказать, что на самом деле стоит оптимизировать. – chmullig

+0

@chmullig, что% тратится на ожидание внешнего процесса? –

1

Вы можете сделать mmap в python, который может справка. Но я подозреваю, что вы допустили ошибку при профилировании, потому что 7k * 1500 за 20 секунд составляет около 0,5 Мбайт/с. Проведите тест, в котором вы пишете случайные строки с одинаковой длиной, и вы увидите, что это намного быстрее, чем это.

Смежные вопросы