2

いくつかのコードで少し問題が発生しています。私はひどいプログラマーなので、私の解決策はおそらくあまり雄弁ではないことを覚えておいてください(そしておそらく私がメモリを使い果たしている理由-私は4ギガバイトを持っていて、スクリプトはゆっくりとそれを満たします)。

ここに問題があります。ディレクトリに約3,500個のファイルがあります。各ファイルは、スペースのない比較的少数または多数の文字を含む可能性のある1行で構成されます(最小のファイルは200バイトであるのに対し、最大のファイルは1.3メガバイトです)。私がやろうとしているのは、これらのファイル間で設定された長さの2つの共通の部分文字列を見つけることです(以下のコードでは13文字です)。それらすべてに共通のサブストリングを探しているのではなく、すべてのファイルが比較されるまで2つの組み合わせを探しているので、一度に2つ実行します。つまり、ファイル間で設定された長さの共通のサブストリングであり、すべてのファイルに共通のサブストリングではありません。

C実装をラップするサフィックスツリーモジュールを使用します(ここ)。最初にディレクトリ内のすべてのファイルのリストを作成し、次にすべての組み合わせがカバーされるように2つの組み合わせを探し、一度に2つのファイルをサフィックスツリーに渡し、次に一般的なサブストリングであるシーケンスを探します。

しかし、なぜそれがゆっくりとメモリを使い果たしているのか、私にはよくわかりません。未使用のもののメモリをなんとかしてクリアするように、コードに修正を加えることができるといいのですが。もちろん、3,500ファイルの処理には長い時間がかかりますが、4ギガバイトのメモリを段階的にいっぱいにすることなく処理できることを願っています。どんな助けでも大歓迎です!これが私がこれまでに持っているコードです:

from suffix_tree import GeneralisedSuffixTree
from itertools import combinations
import glob, hashlib, os

alist = open('tmp.adjlist', 'w')

def read_f(f):
    f = open(f, "r")
    s = str(f.readlines())
    f.close()
    return s

def read_gs(a,b):
    s1 = read_f(a)
    s2 = read_f(b)
    print str(a) + ":" + str(hashlib.md5(s1).hexdigest()) + " --- " + str(b) + ":" + str(hashlib.md5(s2).hexdigest())
    return [s1,s2]

def build_tree(s):
    hlist = []

    stree = GeneralisedSuffixTree(s)

    for shared in stree.sharedSubstrings(13):
        for seq,start,stop in shared:
            hlist.append(hashlib.md5(stree.sequences[seq]).hexdigest())
        hlist = list(set(hlist))
        for h in hlist:
            alist.write(str(h) + " ")
        alist.write('\n')

glist = []

for g in glob.glob("*.g"):
    glist.append(g)

for a,b in list(combinations(glist, 2)):
    s = read_gs(a,b)
    build_tree(s)

alist.close()

os.system("uniq tmp.adjlist network.adjlist && rm tmp.adjlist")

更新#1

更新されたコードは次のとおりです。Pyrceの提案を追加しました。しかし、jogojapanがCコードのメモリリークを特定し、それが私の専門知識をはるかに超えていることを考えると、私ははるかに遅いアプローチをとることになりました。この分野に精通している人がいれば、Cコードを変更してメモリリークや割り当て解除機能を修正する方法を知りたいと思います。PythonのCサフィックスツリーバインディングは非常に価値があると思います。接尾辞木なしでこのスクリプトを介してデータを実行するにはおそらく数日かかるので、誰かが創造的な修正を持っているかどうかを確認することは間違いなくオープンです!

from itertools import combinations
import glob, hashlib, os

def read_f(f):
    with open(f, "r") as openf:
        s = str(openf.readlines())
    return s

def read_gs(a,b):
    s1 = read_f(a)
    s2 = read_f(b)
    print str(a) + ":" + str(hashlib.md5(s1).hexdigest()) + " --- " + str(b) + ":" + str(hashlib.md5(s2).hexdigest())
    return [s1,s2]

def lcs(S1, S2):
    M = [[0]*(1+len(S2)) for i in xrange(1+len(S1))]
    longest, x_longest = 0, 0
    for x in xrange(1,1+len(S1)):
        for y in xrange(1,1+len(S2)):
            if S1[x-1] == S2[y-1]:
                M[x][y] = M[x-1][y-1] + 1
                if M[x][y]>longest:
                    longest = M[x][y]
                    x_longest  = x
            else:
                M[x][y] = 0
    return S1[x_longest-longest: x_longest]

glist = glob.glob("*.g")

for a,b in combinations(glist, 2):
    s = read_gs(a,b)
    p = lcs(s[0],s[1])
    if p != "" and len(p) >= 13:
        with open("tmp.adjlist", "a") as openf:
            openf.write(hashlib.md5(s[1]).hexdigest() + " " + hashlib.md5(s[0]).hexdigest() + "\n")

os.system("uniq tmp.adjlist network.adjlist && rm tmp.adjlist")
4

2 に答える 2

1

使用しているサフィックスツリーパッケージ内にメモリリークがあると合理的に確信しています。

証拠1: valgrind内でPythonを実行してから、この単純なPythonスクリプトを実行する

from suffix_trees import SuffixTree
t = SuffixTree("mississippi")
t = None

このリークを報告しました:

==8800== 1,413 (32 direct, 1,381 indirect) bytes in 1 blocks are definitely lost in loss record 1,265 of 1,374
==8800==    at 0x4A0884D: malloc (vg_replace_malloc.c:263)
==8800==    by 0xBE70AEC: make_helper (suffix_tree.c:193)
==8800==    by 0xBE704B2: SuffixTree_init (python_bindings.c:240)
==8800==    by 0x3F98C9867B: ??? (in /usr/lib64/libpython2.7.so.1.0)
==8800==    by 0x3F98C49A7D: PyObject_Call (in /usr/lib64/libpython2.7.so.1.0)
==8800==    by 0x3F98CD71D6: PyEval_CallObjectWithKeywords (in /usr/lib64/libpython2.7.so.1.0)
==8800==    by 0x3F98C5EB45: ??? (in /usr/lib64/libpython2.7.so.1.0)
==8800==    by 0x3F98C49A7D: PyObject_Call (in /usr/lib64/libpython2.7.so.1.0)
==8800==    by 0x3F98CD93F2: PyEval_EvalFrameEx (in /usr/lib64/libpython2.7.so.1.0)
==8800==    by 0x3F98CDDB2E: PyEval_EvalCodeEx (in /usr/lib64/libpython2.7.so.1.0)
==8800==    by 0x3F98C6D7B5: ??? (in /usr/lib64/libpython2.7.so.1.0)
==8800==    by 0x3F98C49A7D: PyObject_Call (in /usr/lib64/libpython2.7.so.1.0)

証拠2: suffix_tree.cとpython_bindings.cのコードを見ると、make_helper()(を使用して)接尾辞ツリーにメモリを割り当てる関数が見つかりますが、コードのどこにもmalloc単一のものはありません。freepython_bindingsで定義されたPythonタイプに対して定義された割り当て関数と割り当て解除関数を具体的に調べましたが、ツリーオブジェクトに対しては何も見つかりませんでした。ノードオブジェクト用に1つありますが、これはオブジェクトのPythonラッパー部分の割り当てを解除するだけであり、Cの基になる構造体は割り当て解除しません。

証拠3: python_bindings.cのPythonオブジェクトのデータ型定義には、次のようなコメントが付けられています。

/* FIXME: deallocation of this guy! */
static PyTypeObject SuffixTreeType = {
    PyObject_HEAD_INIT(NULL)
    .tp_name      = "_suffix_tree.SuffixTree",
    /* ... */
    .tp_new       = SuffixTree_new,
};

提案:パッケージの作成者に連絡して、問題を認識させることをお勧めします。ウェブページの情報によると、彼らはすでに、ツリー自体とそれに含まれるノードオブジェクトの間に循環依存関係があるはずであるという問題の解決策に取り組んでいます-これは関連する問題であり、おそらくプログラムの理由です現在、割り当て解除は一切実行されていません。

目的に応じてノード間の依存関係はおそらく必要ないため、python_bindings.cのツリーオブジェクト定義に割り当て解除関数を追加して、独自の修正を適用することもできます。

于 2012-07-19T02:26:01.657 に答える
0

元のコードのロジックが意図したとおりに機能するかどうかは100%わかりません。また、リセットするつもりだったリストは確実に増え続けています。変数hlistは、「共有」でループすることなく追加され続けました。セットをそのループに対してローカルにすることで、その問題を修正しました。さらに、リストのセットをリストする必要はありません。最初にセットを使用し、そのセットを反復処理するだけです。Pythonの反復可能なオブジェクトについてもっと学ぶことをお勧めします-オブジェクトを保持するほとんどすべてのPythonデータは(反復可能)です。基本的に、特定の順序が必要な場合を除いて、これらのオブジェクトをlist()する必要はありません。その場合でも、通常はsort()を使用します。

以下のbuild_treeはこれらの問題に対処し、メモリフットプリントを大幅に削減する必要があります。

def build_tree(s):
    stree = GeneralisedSuffixTree(s)

    for shared in stree.sharedSubstrings(13):
        hset = set()
        for seq,start,stop in shared:
            hset.add(hashlib.md5(stree.sequences[seq]).hexdigest())

        for h in hset:
            alist.write(str(h) + " ")
        alist.write('\n')
        # Request cleanup on finished data
        del hset
    # Request cleanup on finished data
    del stree

また、ファイルを使用する場合は「with」キーワードを使用します。これにより、完了時にファイルが閉じられることが保証されます。一方、open / closeは、例外がスローされた場合に後でコードベースをバーフする可能性があります。

def read_f(f):
    with open(f, "r") as openf:
        s = str(openf.readlines())
    return s

そして、私が上で述べたように、あなたが取り戻すすべての変数のlist()ラッパーを削除します-それらはすでに反復可能であり、それらに対してlist()を実行すると、大きな反復可能なアイテムのために大量のメモリ/実行時間を消費する可能性があります。すなわち:

for a,b in list(combinations(glist, 2)):

する必要があります:

for a,b in combinations(glist, 2):

glist = []
for g in glob.glob("*.g"):
    glist.append(g)

次のようになります。

glist = glob.glob("*.g")

これらの変更は役立つはずです。まだメモリが不足している場合はお知らせください。ただし、alistが大きくなるとメモリ使用量が増えるだけで、大きくなりすぎるとフラッシュするはずです。Cコードからもメモリリークが発生している可能性があります(私はそのライブラリを使用していません)。その場合、メモリの問題も発生します。しかし、Pythonコードが原因である可能性がはるかに高くなります。

注: ここに投稿した提案された変更はテストしなかったため、あちこちに小さな構文エラーが存在する可能性があります。

于 2012-07-19T00:34:02.167 に答える