Неоднократно добавляется к большому списку (Python 2.6.6)

У меня есть проект, где я читаю значения ASCII с микроконтроллера через последовательный порт (выглядит так: AA FF BA 11 43 CF и т.д.) Вход поступает быстро (38 двух наборов символов/сек). Я беру этот ввод и добавляю его в список выполняемых операций.

Примерно через 5 часов мой список вырос до ~ 855000 записей.

Мне дано понять, что чем больше список становится, тем медленнее выполняются операции списка. Мое намерение состоит в том, чтобы этот пробный прогон в течение 24 часов, что должно приводить к результатам 3M.

Есть ли более эффективный и быстрый способ добавления в список, а затем list.append()?

Спасибо всем.

Ответ 1

Мне дано понять, что чем больше список становится, тем медленнее выполняются операции списка.

Это не правда в целом. Списки в Python, несмотря на имя, не связаны списки, но массивы. Существуют операции, которые являются O (n) на массивах (например, копирование и поиск), но вы, похоже, не используете их. Как правило: если он широко используется и идиоматично, некоторые умные люди пошли и выбрали разумный способ сделать это. list.append является широко используемым встроенным (и базовая функция C также используется в других местах, например, в списках). Если бы был более быстрый способ, он уже был бы использован.

Как вы увидите, когда вы проверяете исходный код, списки группируются, т.е. когда они изменяются, они выделяют больше, чем необходимо для одного, поэтому следующие n элементов могут быть добавлены без необходимости другого изменения размера (что равно O (n)). Рост не является постоянным, он пропорционален размеру списка, поэтому изменение размера становится все реже, поскольку список увеличивается. Здесь фрагмент из listobject.c:list_resize, который определяет общее назначение:

/* This over-allocates proportional to the list size, making room
 * for additional growth.  The over-allocation is mild, but is
 * enough to give linear-time amortized behavior over a long
 * sequence of appends() in the presence of a poorly-performing
 * system realloc().
 * The growth pattern is:  0, 4, 8, 16, 25, 35, 46, 58, 72, 88, ...
 */
new_allocated = (newsize >> 3) + (newsize < 9 ? 3 : 6);

Как отмечает Mark Ransom, более старые версии Python (< 2.7, 3.0) имеют ошибку, которая делает саботаж GC таким. Если у вас такая версия Python, вы можете отключить gc. Если вы не можете, потому что вы генерируете слишком много мусора (это ускоряет пересчет), вам не повезло.

Ответ 2

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

Ответ 3

Добавление в список python имеет постоянную стоимость. Это не влияет на количество элементов в списке (теоретически). На практике добавление к списку будет медленнее, если у вас закончится нехватка памяти, и система начнет замену.

http://wiki.python.org/moin/TimeComplexity

Было бы полезно понять, почему вы действительно добавляете вещи в список. Что вы планируете делать с предметами. Если вам не нужны все они, вы можете создать кольцевой буфер, если вам не нужно делать вычисления, вы можете записать список в файл и т.д.

Ответ 4

Прежде всего, 38 двухсимвольных наборов в секунду, 1 стоповый бит, 8 бит данных и отсутствие четности - всего 760 бод, а не скорость вообще.

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

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

Ответ 5

Может быть, быстрее использовать numpy, если вы знаете, как долго будет массив, и вы можете конвертировать шестнадцатеричные коды в ints:

import numpy
a = numpy.zeros(3000000, numpy.int32)
for i in range(3000000):
   a[i] = int(scanHexFromSerial(),16)

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