web-dev-qa-db-ja.com

大きなリストに繰り返し追加する(Python 2.6.6)

ASCIIマイクロコントローラーからのシリアルポート経由の値(このように見えます:AA FF BA 11 43 CFなど))を読み込んでいるプロジェクトがあります。入力がすぐに入力されます(38 2文字セット/秒)私はこの入力を取得し、それをすべての測定の実行リストに追加しています。

約5時間後、私のリストは約855000エントリに増えました。

リストが大きくなるほど、リストの操作が遅くなることを理解しています。私の意図は、このテストを24時間実行して、約3Mの結果が得られるようにすることです。

リストに追加してからlist.append()を追加するより効率的で高速な方法はありますか?

みんな、ありがとう。

20
Michael

リストが大きくなると、リストの操作が遅くなることを理解しています。

それは一般的には当てはまりません。 Pythonのリストは、名前にもかかわらず、リンクされたリストではなく配列です。配列に対してO(n)である操作があります(コピーおよび検索、例)しかし、あなたはこれらのどれも使用していないようです。経験則として:それが広く使用されており、慣用的である場合、一部の賢い人々はそれを行うためにスマートな方法を選びました。list.appendは広く-組み込み関数を使用(そして、基になるC関数は、リスト内包表記など、他の場所でも使用されます)より速い方法があった場合、それは既に使用されています。

ソースコード を調べるとわかるように、リストは全体に割り当てられています。つまり、リストのサイズが変更されると、1つのアイテムに必要以上に割り当てられるため、別のサイズ変更をせずに次の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を無効にしたい場合があります。生成されたガベージが多すぎて(参照カウントがずれて)できない場合は、運が悪いです。

34
user395760

検討する必要があるのは、収集されたデータをファイルに書き込むことです。パフォーマンスに影響するかどうかはわかりません(または本当に気にします)が、電源が切れてもすべてのデータが失われないようにするのに役立ちます。すべてのデータを取得したら、それをファイルから吸い出して、リスト、配列、または派手な行列などの処理のためにジャムすることができます。

12
nmichaels

pythonリストへの追加には一定のコストがあります。リスト内のアイテムの数には影響されません(理論的には)。実際には、リストの追加が遅くなると、メモリとシステムがスワッピングを開始します。

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

実際にリストに追加する理由を理解しておくと役に立ちます。アイテムをどうするつもりですか。それらのすべてが必要ない場合は、リングバッファーを作成できます。計算を行う必要がない場合は、リストをファイルに書き込むなどできます。

2
cellcortex

配列の長さを知っていて、16進コードをintに変換できる場合は、numpyを使用する方が速い場合があります。

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

これにより、整数の配列が残ります(hex()を使用して16進数に変換し直すことができます)。ただし、アプリケーションによっては、問題なく動作する場合があります。

0
flionetti

まず、1秒あたり38個の2文字セット、1ストップビット、8データビット、パリティなしは、わずか760ボーであり、まったく高速ではありません。

とにかく、私の提案は、リストが大きすぎるのが心配な場合や、1つの巨大なリストを使用したくない場合は、リストが特定のサイズに達したらディスクに保存し、新しいリストを開始するだけです。すべてのデータを取得し、データの受信が完了したら、すべてのリストを1つに結合します。

サブリストを完全にスキップしてnmichaelsの提案にそのまま従うこともできますが、取得したデータをファイルに書き込み、小さな循環バッファーを使用して、まだ書き込まれていない受信データを保持します。

0
JAB