Чтение нескольких засоленных данных Python одновременно, буферизация и переводы строк? - PullRequest
9 голосов
/ 01 апреля 2011

, чтобы дать вам контекст:

У меня есть большой файл f, размером в несколько гигабайт.Он содержит последовательные метки различных объектов, которые были сгенерированы при запуске

for obj in objs: cPickle.dump(obj, f)

. Я хочу воспользоваться буферизацией при чтении этого файла.Что я хочу, так это читать несколько выбранных объектов в буфер одновременно.Каков наилучший способ сделать это?Я хочу аналог readlines(buffsize) для маринованных данных.На самом деле, если выбранные данные действительно разделены символом новой строки, можно использовать readlines, но я не уверен, что это правда.

Другой вариант, который я имею в виду, - это dumps() сначала отфильтрованный объект до строкиа затем записать строки в файл, каждый из которых разделен новой строкой.Чтобы прочитать файл обратно, я могу использовать readlines() и loads().Но я боюсь, что засоленный объект может иметь символ "\n", и это приведет к отказу от этой схемы чтения файлов.Мой страх необоснован?

Один из вариантов - выделить его как огромный список объектов, но для этого потребуется больше памяти, чем я могу себе позволить.Установка может быть ускорена многопоточностью, но я не хочу идти туда, пока я не установлю буферизацию должным образом.Какова «лучшая практика» для подобных ситуаций.

РЕДАКТИРОВАТЬ: я также могу читать в необработанных байтах в буфер и вызывать нагрузки на него, но мне нужно знать, сколько байтов этого буфера было использовано нагрузкамитак что я могу выбросить голову.

Ответы [ 4 ]

6 голосов
/ 01 апреля 2011

Тебе не нужно ничего делать, я думаю.

>>> import pickle
>>> import StringIO
>>> s = StringIO.StringIO(pickle.dumps('apples') + pickle.dumps('bananas'))
>>> pickle.load(s)
'apples'
>>> pickle.load(s)
'bananas'
>>> pickle.load(s)

Traceback (most recent call last):
  File "<pyshell#25>", line 1, in <module>
    pickle.load(s)
  File "C:\Python26\lib\pickle.py", line 1370, in load
    return Unpickler(file).load()
  File "C:\Python26\lib\pickle.py", line 858, in load
    dispatch[key](self)
  File "C:\Python26\lib\pickle.py", line 880, in load_eof
    raise EOFError
EOFError
>>> 
5 голосов
/ 01 апреля 2011

file.readlines () возвращает список всего содержимого файла. Вы хотите прочитать несколько строк одновременно. Я думаю этот наивный код должен восстановить ваши данные:

import pickle
infile = open('/tmp/pickle', 'rb')
buf = []
while True:
    line = infile.readline()
    if not line:
        break
    buf.append(line)
    if line.endswith('.\n'):
        print 'Decoding', buf
        print pickle.loads(''.join(buf))
        buf = []

Если у вас есть какой-либо контроль над программой, которая генерирует соленья, я бы выбрал одно из:

  1. Используйте модуль shelve.
  2. Напечатайте длину (в байтах) каждого рассола перед записью его в файл, чтобы вы точно знали, сколько байтов нужно прочитать за каждый раз.
  3. То же, что и выше, но записать список целых чисел в отдельный файл, чтобы вы могли использовать эти значения в качестве индекса в файле, содержащем соленья.
  4. Выберите список из K объектов одновременно. Запишите длину этого рассола в байтах. Напиши рассол. Повторите.

Кстати, я подозреваю, что встроенная буферизация file должна дать вам 99% прироста производительности, который вы ищете.

Если вы уверены, что ввод-вывод блокирует вас, вы задумывались о том, чтобы попробовать mmap() и позволить ОС обрабатывать упаковку в блоки за один раз?

#!/usr/bin/env python

import mmap
import cPickle

fname = '/tmp/pickle'
infile = open(fname, 'rb')
m = mmap.mmap(infile.fileno(), 0, access=mmap.ACCESS_READ)
start = 0
while True:
    end = m.find('.\n', start + 1) + 2
    if end == 1:
        break
    print cPickle.loads(m[start:end])
    start = end
2 голосов
/ 01 апреля 2011

Если вы хотите добавить буферизацию в любой файл, откройте его с помощью io.open().Вот пример, который будет читать из базового потока в кусках 128K.Каждый вызов cPickle.load() будет выполняться из внутреннего буфера до тех пор, пока он не будет исчерпан, затем из базового файла будет считан другой фрагмент:

import cPickle
import io

buf = io.open('objects.pkl', 'rb', buffering=(128 * 1024))
obj = cPickle.load(buf)
2 голосов
/ 01 апреля 2011

Возможно, вы захотите взглянуть на модуль shelve . Он использует модуль базы данных, такой как dbm, для создания словаря объектов на диске. Сами объекты по-прежнему сериализуются с использованием pickle. Таким образом, вы можете читать наборы объектов вместо одного большого рассола за раз.

...