Сериализация списка 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
правильно.