Как читать строки из файла mmapped? - PullRequest
21 голосов
/ 16 ноября 2011

Похоже, что интерфейс mmap поддерживает только readline (). Если я пытаюсь перебрать объект, я получаю символ вместо целых строк.

Каким будет «питонный» метод чтения файла mmap построчно?

import sys
import mmap
import os


if (len(sys.argv) > 1):
  STAT_FILE=sys.argv[1]
  print STAT_FILE
else:
  print "Need to know <statistics file name path>"
  sys.exit(1)


with open(STAT_FILE, "r") as f:
  map = mmap.mmap(f.fileno(), 0, prot=mmap.PROT_READ)
  for line in map:
    print line # RETURNS single characters instead of whole line

Ответы [ 4 ]

30 голосов
/ 16 ноября 2011

Наиболее краткий способ перебора строк mmap -

with open(STAT_FILE, "r+b") as f:
    map_file = mmap.mmap(f.fileno(), 0, prot=mmap.PROT_READ)
    for line in iter(map_file.readline, b""):
        # whatever

. Обратите внимание, что в Python 3 параметр sentinel iter() должен иметь тип bytes, тогда как в Python2 это должно быть str (то есть "" вместо b"").

15 голосов
/ 16 ноября 2011

Я изменил ваш пример следующим образом:

with open(STAT_FILE, "r+b") as f:
        m=mmap.mmap(f.fileno(), 0, prot=mmap.PROT_READ)
        while True:
                line=m.readline()
                if line == '': break
                print line.rstrip()

Предложения:

  • Не вызывайте переменную map, это встроеннаяв функции.
  • Откройте файл в режиме r+b , как в примере с Python на странице справки mmap.В нем говорится: В любом случае вы должны предоставить дескриптор файла для файла, открытого для обновления .См. http://docs.python.org/library/mmap.html#mmap.mmap.
  • Лучше не использовать UPPER_CASE_WITH_UNDERSCORES имена глобальных переменных, как указано в Имена глобальных переменных в https://www.python.org/dev/peps/pep-0008/#global-variable-names. В других языках программирования (например, C),константы часто пишутся в верхнем регистре.

Надеюсь, это поможет.

Редактировать: Я провел несколько тестов синхронизации в Linux, потому что комментарий вызвал у меня любопытство.Ниже приведено сравнение времени выполнения 5 последовательных прогонов текстового файла размером 137 МБ.

Обычный доступ к файлу:

real    2.410 2.414 2.428 2.478 2.490
sys     0.052 0.052 0.064 0.080 0.152
user    2.232 2.276 2.292 2.304 2.320

mmap доступ к файлу:

real    1.885 1.899 1.925 1.940 1.954
sys     0.088 0.108 0.108 0.116 0.120
user    1.696 1.732 1.736 1.744 1.752

Эти сроки не включают в себя оператор print (я его исключил).Следуя этим цифрам, я бы сказал, что доступ к файлам, отображаемым в память, происходит немного быстрее.

Редактировать 2: Используя python -m cProfile test.py Я получил следующие результаты:

5432833    2.273    0.000    2.273    0.000 {method 'readline' of 'file' objects}
5432833    1.451    0.000    1.451    0.000 {method 'readline' of 'mmap.mmap' objects}

Если я не ошибаюсь, mmap немного быстрее.

Кроме того, кажется, что not len(line) работает хуже, чем line == '', по крайней мере, так я интерпретирую вывод профилировщика.

1 голос
/ 16 ноября 2011

Следующее достаточно кратко:

with open(STAT_FILE, "r") as f:
    m = mmap.mmap(f.fileno(), 0, prot=mmap.PROT_READ)
    while True:
        line = m.readline()  
        if line == "": break
        print line
    m.close()

Обратите внимание, что line сохраняет символ новой строки, поэтому вы можете удалить его. Это также причина, по которой if line == "" делает правильные вещи (пустая строка возвращается как "\n").

Причина, по которой исходная итерация работает так, как она работает, заключается в том, что mmap пытается выглядеть как файл и строка. Это похоже на строку для целей итерации.

Понятия не имею, почему он не может (или не хочет) предоставить readlines()/xreadlines().

0 голосов
/ 29 декабря 2018

32-разрядный Python 2.7 в Windows - это более чем в два раза быстрее в отображенном файле:

В 27 МБ, 509 тыс. Текстовых файлах (моя функция 'разбора' не интересна, в основном это просто readline () очень быстро):

with open(someFile,"r") as f:
    if usemmap:
        m=mmap.mmap(f.fileno(), 0, access=mmap.ACCESS_READ)
    else:
        m=f
        e.parse(m)

С MMAP:

read in 0.308000087738

Без MMAP:

read in 0.680999994278
...