быстрое перемещение данных из файла в какой-нибудь StringIO - PullRequest
7 голосов
/ 23 ноября 2011

В Python у меня есть поток файлов, и я хочу скопировать некоторую его часть в StringIO.Я хочу, чтобы это было как можно быстрее, с минимальной копией.

Но если я сделаю:

data = file.read(SIZE)
stream = StringIO(data)

Я думаю, что 2 копии были сделаны, нет?Одна копия в данные из файла, другая копия внутри StringIO во внутренний буфер.Могу ли я избежать одной из копий?Мне не нужно временное data, поэтому я думаю, что одной копии должно быть достаточно

Ответы [ 4 ]

8 голосов
/ 23 ноября 2011

Короче говоря: вы не можете избежать 2 копий, используя StringIO.

Некоторые предположения:

  • Вы используете cStringIO, иначе было бы глупо так много оптимизировать.
  • Вам нужна скорость, а не эффективность памяти. Если нет, см. Решение Jakob Bowyer's или используйте вариант, используя file.read(SOME_BYTE_COUNT), если ваш файл является двоичным.
  • Вы уже заявили об этом в комментариях, но для полноты: вы хотите на самом деле редактировать содержимое, а не просто просматривать его.

Длинный ответ : Поскольку строки python являются неизменяемыми, а буфер StringIO - нет, копия должна быть сделана рано или поздно; в противном случае вы изменили бы неизменный объект! Для того, что вы хотите сделать возможным, объект StringIO должен иметь специальный метод, который читает непосредственно из файлового объекта, заданного в качестве аргумента. Нет такого метода.

За пределами из StringIO существуют решения, позволяющие избежать дополнительной копии. Вдобавок ко всему, это будет считывать файл непосредственно в модифицируемый байтовый массив, без дополнительной копии:

import numpy as np
a = np.fromfile("filename.ext", dtype="uint8")

Работать с ним может быть затруднительно в зависимости от предполагаемого использования, поскольку это массив значений от 0 до 255, а не массив символов. Но он функционально эквивалентен объекту StringIO, и использование np.fromstring, np.tostring, np.tofile и разделение на нотации приведут вас туда, куда вы хотите. Вам также могут понадобиться np.insert, np.delete и np.append.

Я уверен, что есть другие модули, которые будут делать подобные вещи.

TIMEIT:

Насколько все это действительно имеет значение ? Ну что ж, посмотрим. Я сделал файл размером 100 МБ, largefile.bin. Затем я читаю в файле оба метода и меняю первый байт.

$ python -m timeit -s "import numpy as np" "a = np.fromfile('largefile.bin', 'uint8'); a[0] = 1"
10 loops, best of 3: 132 msec per loop
$ python -m timeit -s "from cStringIO import StringIO" "a = StringIO(); a.write(open('largefile.bin').read()); a.seek(0); a.write('1')"
10 loops, best of 3: 203 msec per loop

Так что в моем случае использование StringIO на 50% медленнее, чем использование numpy.

Наконец, для сравнения, отредактируйте файл напрямую:

$ python -m timeit "a = open('largefile.bin', 'r+b'); a.seek(0); a.write('1')"
10000 loops, best of 3: 29.5 usec per loop

Итак, это почти в 4500 раз быстрее. Конечно, это очень зависит от того, что вы собираетесь делать с файлом. Изменение первого байта вряд ли является представительным. Но при использовании этого метода у вас есть преимущество перед двумя другими, и, поскольку большинство ОС имеют хорошую буферизацию дисков, скорость также может быть очень хорошей.

(Если вам не разрешено редактировать файл и вы хотите избежать затрат на создание рабочей копии, есть несколько возможных способов увеличить скорость. Если вы можете выбрать файловую систему, Btrfs имеет операцию копирования файла 1053 * copy-on-write - практически мгновенное создание копии файла. Этого же эффекта можно добиться, используя снимок LVM любой файловой системы.)

6 голосов
/ 23 ноября 2011

Нет, дополнительная копия не сделана.Буфер, используемый для хранения данных, такой же.И data, и внутренний атрибут, доступный с помощью StringIO.getvalue(), являются разными именами для одних и тех же данных.

Python 2.7 (r27:82500, Jul 30 2010, 07:39:35) 
[GCC 4.1.2 20080704 (Red Hat 4.1.2-48)] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import StringIO
>>> data = open("/dev/zero").read(1024)
>>> hex(id(data))
'0xea516f0'
>>> stream = StringIO.StringIO(data)
>>> hex(id(stream.getvalue()))
'0xea516f0'

Быстрый просмотр источника показывает, что cStringIO неТакже создайте копию при создании, но она делает копию при вызове cStringIO.getvalue(), поэтому я не могу повторить приведенную выше демонстрацию.

2 голосов
/ 23 ноября 2011

Может быть, вы ищете буфер / память :

>>> data = file.read(SIZE)
>>> buf = buffer(data, 0, len(data))

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

Более подробную информацию вы можете найти в этом связанном вопросе .

Редактировать: В этом сообщении в блоге Я нашел через Reddit, некоторые дополнительные сведения приведены по той же проблеме:

>>> f = open.(filename, 'rb')
>>> data = bytearray(os.path.getsize(filename))
>>> f.readinto(data)

По словам автора, дополнительная копия не создается, и данные могут бытьизменено, поскольку bytearray является изменяемым.

0 голосов
/ 23 ноября 2011
stream = StringIO()
for line in file:
    stream.write(line + "\n")
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...