Сериализация списка Python - самый быстрый метод - PullRequest
11 голосов
/ 17 февраля 2009

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

Какой метод самый быстрый и почему?

  1. Использование import в файле .py, который просто содержит список, назначенный переменной
  2. Использование cPickle s load
  3. Какой-то другой метод (возможно, numpy?)

Кроме того, как можно надежно оценивать такие вещи?

Приложение: Надежно измерить это, потому что import кэшируется, поэтому его нельзя выполнить несколько раз в тесте. Загрузка с помощью pickle также становится быстрее после первого раза, вероятно, из-за предварительного просмотра страниц ОС. Загрузка 1 миллиона номеров с помощью cPickle занимает 1,1 с при первом запуске и 0,2 с при последующих выполнениях сценария.

Интуитивно я чувствую, что cPickle должно быть быстрее, но я бы оценил цифры (думаю, это довольно сложная задача для измерения).

И да, для меня важно, чтобы это выполнялось быстро.

Спасибо

Ответы [ 6 ]

7 голосов
/ 17 февраля 2009

Я думаю, cPickle будет самым быстрым, если вам действительно нужна вещь из списка.

Если вы можете использовать массив , который является встроенным типом последовательности, я рассчитал это за четверть секунды для 1 миллиона целых чисел:

from array import array
from datetime import datetime

def WriteInts(theArray,filename):
    f = file(filename,"wb")
    theArray.tofile(f)
    f.close()

def ReadInts(filename):
    d = datetime.utcnow()
    theArray = array('i')
    f = file(filename,"rb")
    try:
        theArray.fromfile(f,1000000000)
    except EOFError:
        pass
    print "Read %d ints in %s" % (len(theArray),datetime.utcnow() - d)
    return theArray

if __name__ == "__main__":
    a = array('i')
    a.extend(range(0,1000000))
    filename = "a_million_ints.dat"
    WriteInts(a,filename)
    r = ReadInts(filename)
    print "The 5th element is %d" % (r[4])
3 голосов
/ 17 февраля 2009

Для тестирования см. Модуль timeit в стандартной библиотеке Python. Чтобы увидеть, что является самым быстрым способом, реализуйте все возможные способы и измеряйте их с течением времени.

Случайная мысль: в зависимости от того, что именно вы делаете, вы можете быстрее всего хранить «наборы целых чисел» в стиле, используемом в .newsrc файлах:

1, 3-1024, 11000-1200000

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

2 голосов
/ 24 февраля 2009

Вам нужно всегда загружать весь файл? Если нет, то upack_from () может быть лучшим решением. Предположим, что у вас есть 1000000 целых чисел, но вы хотите загрузить только те, от 50000 до 50099, вам нужно сделать:

import struct
intSize = struct.calcsize('i') #this value would be constant for a given arch
intFile = open('/your/file.of.integers')
intTuple5K100 = struct.unpack_from('i'*100,intFile,50000*intSize)
2 голосов
/ 17 февраля 2009

Чтобы помочь вам с синхронизацией, библиотека Python предоставляет модуль timeit:

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

Пример (из руководства), в котором сравнивается стоимость использования hasattr() против try/except для проверки на наличие и отсутствие атрибутов объекта:

% timeit.py 'try:' '  str.__nonzero__' 'except AttributeError:' '  pass'
100000 loops, best of 3: 15.7 usec per loop
% timeit.py 'if hasattr(str, "__nonzero__"): pass'
100000 loops, best of 3: 4.26 usec per loop
% timeit.py 'try:' '  int.__nonzero__' 'except AttributeError:' '  pass'
1000000 loops, best of 3: 1.43 usec per loop
% timeit.py 'if hasattr(int, "__nonzero__"): pass'
100000 loops, best of 3: 2.23 usec per loop
2 голосов
/ 17 февраля 2009

"Как можно надежно оценить такие вещи?"

Я не понимаю вопрос.

Вы пишете кучу маленьких функций для создания и сохранения списка в различных формах.

Вы пишете несколько маленьких функций для загрузки ваших списков в их различных формах.

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

Вы суммируете свои данные в небольшом отчете.

Что ненадежного в этом?

Вот некоторые несвязанные вопросы, которые показывают, как измерить и сравнить производительность.

Преобразовать список целых чисел в одно число?

Конкатенация строк и подстановка строк в Python

1 голос
/ 17 февраля 2009

cPickle будет самым быстрым, поскольку он сохраняется в двоичном формате, и никакой реальный код Python не должен быть проанализирован.

Другие преимущества заключаются в том, что он более безопасен (поскольку он не выполняет команды), и у вас нет проблем с правильной настройкой $PYTHONPATH.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...