Сериализация списка Python - самый быстрый метод

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

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

  • Используя import в .py файле, который просто содержит список, назначенный переменной
  • Использование cPickle load
  • Другой метод (возможно, numpy?)

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

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

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

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

Спасибо

Ответ 1

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

Если вы можете использовать array, который является встроенным типом последовательности, я назначил это на четверть секунды для 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])

Ответ 2

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

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

1, 3-1024, 11000-1200000

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

Ответ 3

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

У меня вопрос не возникает.

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

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

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

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

Что ненадежно?

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

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

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

Ответ 4

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

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

Пример (из руководства), который сравнивает стоимость использования hasattr() vs. 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

Ответ 5

Вам нужно всегда загружать весь файл? Если нет, 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)

Ответ 6

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

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