2

일부 코드로 인해 문제가 발생합니다. 필자는 끔찍한 프로그래머이므로 염두에 두지 마십시오. 아마도 내 솔루션은별로 웅변 적이 지 않습니다. 메모리가 부족한 이유는 아마도 4GB이며 스크립트가 천천히 채 웁니다.파이썬 메모리 부족 (접미사 트리 사용)

여기에 문제가 있습니다. 디렉토리에 약 3,500 개의 파일이 있습니다. 각 파일은 공백없이 비교적 적은 수의 문자를 가질 수있는 한 줄로 구성됩니다 (가장 작은 파일은 1.3 바이트에서 가장 큰 파일 대 200 바이트입니다). 내가 뭘 하려는지 설정 길이의 시간에 두 파일 사이에 일반적인 하위 문자열을 찾을 수 있습니다 (그것의 13 문자 아래의 코드에서). 나는 모든 파일 중에서 공통 부분 문자열을 찾고있는 것이 아니기 때문에 한 번에 두 개를 수행합니다. 그러나 모든 파일을 비교할 때까지 두 개를 조합하는 것이 좋습니다. 즉, 파일 사이에 설정된 길이의 모든 공통 부분 문자열이며 모든 부분에 공통된 부분 문자열이 아닙니다.

C 구현 (over here)을 래핑하는 접미어 트리 모듈을 사용합니다. 먼저 디렉토리의 모든 파일 목록을 만든 다음 두 조합을 조합하여 모든 조합을 다룹니다. 한 번에 두 파일을 접미어 트리로 전달한 다음 공통 부분 문자열 인 시퀀스를 찾습니다.

그러나 실제로 천천히 왜 메모리가 부족한지는 모르겠습니다. 코드에서 수정할 수 있기를 바래서 어떻게 든 사용하지 않는 메모리를 정리할 수 있었으면 좋겠습니까? 물론 3,500 개의 파일을 처리하는 데 오랜 시간이 걸리지 만, 4GB의 메모리를 점진적으로 채우지 않고도 수행 할 수 있기를 바랍니다. 어떤 도움이라도 대단히 감사하겠습니다!

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 코드의 메모리 누수를 확인한 후 이것이 내 전문 지식의 범위를 벗어나면 훨씬 느린 접근 방식을 사용하게되었습니다. 누구도이 영역에 대해 잘 알고 있다면 Python 용 C 접미사 트리 바인딩이 매우 가치 있다고 생각하므로 메모리 누수 또는 할당 해제 함수를 수정하기 위해 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") 

답변

1

사용하는 접미어 트리 패키지 내에 메모리 누수가 있음을 합리적으로 확신합니다.

증거 1 :

==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 :

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

이 누출을보고 Valgrind의 내부에 파이썬을 실행 한 다음이 간단한 파이썬 스크립트를 실행 당신은 코드를 볼 때 suffix_tree.c 및 python_bindings.c에서 make_helper() 함수를 찾을 수 있습니다.이 함수는 접미어 트리에 대한 메모리를 할당합니다 (malloc 사용). 그러나 th 코드의 어느 곳에서나 free은 하나가 아닙니다. python_bindings에 정의 된 Python 유형에 대해 정의 된 할당 및 할당 해제 함수를 자세히 살펴 보았지만 트리 객체에 대해서는 아무 것도 찾을 수 없었습니다. 가 노드 오브젝트에 대한 하나하지만 만 아니라 개체 C.

의 기본 구조

증거 3 파이썬 래퍼 부분 할당 해제 :가 python_bindings.c에서 파이썬 객체 데이터 유형 정의가 갖고

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

제안 : 첨부 코멘트를 말하는 패키지의 저자에 문의하여 문제들을 인식하게 할 수 있습니다. 웹 페이지의 정보에 따르면, 그들은 이미 트리 자체와 그 노드 객체에 포함 된 노드 객체 사이의 주기적 종속성이 있어야한다는 문제에 대한 해결책을 개발하고 있습니다. 이것은 관련 문제이며 아마도 프로그램이 현재 할당 해제를 수행하지 않습니다.

목적에 따라 노드 - 트리 간 의존성이 필요하지 않으므로 python_bindings.c의 트리 객체 정의에 할당 해제 기능을 추가하여 자신 만의 수정을 적용 할 수 있습니다.

0

원본 코드의 논리가 사용자가 의도 한대로 정확하게 수행되지는 않습니다. 재설정하려는 의미가 지속적으로 증가하는 목록이 분명히 있습니다. 변수 hlist는 'shared'에 대한 루프없이 추가되었습니다. 해당 루프에 대해 로컬 세트를 작성하면 해당 문제가 해결됩니다. 또한 목록 집합을 나열 할 필요가 없습니다. 먼저 집합을 사용하여 시작하고 해당 집합에 반복자를 사용하십시오. 나는 파이썬에서 반복 가능한 객체에 대해 더 많이 배울 것을 제안한다 - 거의 모든 파이썬 데이터 보유 객체 (iterable). 기본적으로 sorting()을 사용하여 특정 순서가 필요하지 않은 경우에도 해당 객체를 list() 할 필요가 없습니다.

아래 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 

또한 키워드 '와'사용 - 당신이 완료되면 파일이 닫힙니다 보장 - 어디 오픈/예외가 발생하면 나중에 코드베이스를 발프 수 가깝습니다.

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

그리고 제가 위에서 언급 한 바와 같이, 목록을() 당신이 다시 가져 오는 모든 변수에 래퍼 제거 - 그들은/반복 가능한 메모리의 톤을 소비 할 수있는 그들에 목록을()하고 이미에 대한 실행 시간입니다 큰 iterable 항목. 예 :

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") 

이러한 변화는 도움이 될 것입니다, 당신은 여전히 ​​실행하는 경우 알려주세요 메모리가 부족하지만 이제는 성장해야합니다. 알리 스트가 커짐에 따라 메모리 사용량이 커지면 너무 커지면 플러시되어야합니다. C 코드에서 메모리 누수가 생길 수도 있습니다 (이 라이브러리를 사용하지 않았습니다).이 경우 메모리 문제가 발생할 수도 있습니다. 그러나 파이썬 코드가 범인 일 가능성이 훨씬 큽니다.

참고 : 여기에 게시 된 제안 된 변경 사항을 테스트하지 않았으므로 여기저기서 약간의 구문 오류가있을 수 있습니다.