2012-12-17 2 views
0

Here is the full repository. 이것은 postgresql-simple 데이터베이스 바인딩을 사용하여 50000 개의 임의의 것들을 데이터베이스에 삽입하는 매우 간단한 테스트입니다. 그것은 MonadRandom을 사용하고 게으른 것을 생성 할 수 있습니다.이 코드는 왜 많은 양의 힙을 소비합니까?

Here is the lazy Thing generator.

insertThings c = do 
    ts <- genThings 
    withTransaction c $ do 
    executeMany c "insert into things (a, b, c) values (?, ?, ?)" $ map (\(Thing ta tb tc) -> (ta, tb, tc)) $ take 50000 ts 

Here is case2, 단지 표준 출력에 물건을 덤프 : : 나는 아주 나쁜 GC 시간이 첫 번째 경우

main = do 
    ts <- genThings 
    mapM print $ take 50000 ts 

: 것 생성기를 사용하여 코드의

Here is case1 특정 조각

cabal-dev/bin/posttest +RTS -s  
    1,750,661,104 bytes allocated in the heap 
    619,896,664 bytes copied during GC 
     92,560,976 bytes maximum residency (10 sample(s)) 
     990,512 bytes maximum slop 
      239 MB total memory in use (0 MB lost due to fragmentation) 

            Tot time (elapsed) Avg pause Max pause 
    Gen 0  3323 colls,  0 par 11.01s 11.46s  0.0034s 0.0076s 
    Gen 1  10 colls,  0 par 0.74s 0.77s  0.0769s 0.2920s 

    INIT time 0.00s ( 0.00s elapsed) 
    MUT  time 2.97s ( 3.86s elapsed) 
    GC  time 11.75s (12.23s elapsed) 
    RP  time 0.00s ( 0.00s elapsed) 
    PROF time 0.00s ( 0.00s elapsed) 
    EXIT time 0.00s ( 0.00s elapsed) 
    Total time 14.72s (16.09s elapsed) 

    %GC  time  79.8% (76.0% elapsed) 

    Alloc rate 588,550,530 bytes per MUT second 

    Productivity 20.2% of total user, 18.5% of total elapsed 

두 번째 경우에는 시간이 많이 걸리는 반면 :

cabal-dev/bin/dumptest +RTS -s > out 
    1,492,068,768 bytes allocated in the heap 
     7,941,456 bytes copied during GC 
     2,054,008 bytes maximum residency (3 sample(s)) 
      70,656 bytes maximum slop 
       6 MB total memory in use (0 MB lost due to fragmentation) 

            Tot time (elapsed) Avg pause Max pause 
    Gen 0  2888 colls,  0 par 0.13s 0.16s  0.0001s 0.0089s 
    Gen 1   3 colls,  0 par 0.01s 0.01s  0.0020s 0.0043s 

    INIT time 0.00s ( 0.00s elapsed) 
    MUT  time 2.00s ( 2.37s elapsed) 
    GC  time 0.14s ( 0.16s elapsed) 
    RP  time 0.00s ( 0.00s elapsed) 
    PROF time 0.00s ( 0.00s elapsed) 
    EXIT time 0.00s ( 0.00s elapsed) 
    Total time 2.14s ( 2.53s elapsed) 

    %GC  time  6.5% (6.4% elapsed) 

    Alloc rate 744,750,084 bytes per MUT second 

    Productivity 93.5% of total user, 79.0% of total elapsed 

힙 프로파일 링을 적용하려했지만 아무 것도 이해하지 못했습니다. 그것은 50000 건 모두가 메모리에서 먼저 생성 된 다음 쿼리가있는 ByteString으로 변환 된 다음이 문자열이 데이터베이스로 보내집니다. 그런데 왜 그렇게됩니까? 유죄 판결을 내리는 방법은 무엇입니까?

GHC 버전은 7.4.2

편집 앨범 플래그 내가 formatMany 및 50K 것들과 프로필을 확인했습니다 (샌드 박스에 음모-DEV에 의해 컴파일 된) 모든 라이브러리 및 패키지 자체

+1

첫 번째 사례와 두 번째 사례간에 어떤 코드가 다른지 이해할 수 없습니다. 1) 자체 포함 된 코드 또는 적어도 명시적인 문구를 사례에 게시 할 수 있습니까? 2) GHC 버전 3) 컴파일러 플래그. –

+0

이것은 나에게 거의 힙을 사용하지 않고 - 최적화없이 컴파일하고 있습니까? 그것은 문제가 될 것입니다. –

+0

-O2를 사용하여 모든 라이브러리를 다시 컴파일했지만 효과가 없습니다. – s9gf4ult

답변

1

에 대한 -O2입니다. 메모리가 꾸준히 증가한 다음 빠르게 떨어집니다. 사용 된 최대 메모리는 40MB가 약간 넘습니다. 주요 코스트 센터는 buildQuery와 escapeStringConn과 toRow입니다. 데이터의 절반은 ARR_WORDS (바이트 문자열), 작업 및 목록입니다.

formatMany 중첩 된 동작 목록에서 어셈블 된 조각들로 거의 하나가 길게 만듭니다. ByteString 작업은 ByteString 빌더로 변환되어 최종 길이가 엄격한 ByteString을 생성 할 때까지 ByteStrings을 유지합니다. 이 ByteString은 최종 BS가 구성 될 때까지 수명이 길다.

문자열을 libPQ로 이스케이프 처리해야하므로 일반 액션이 아닌 BS이 libPQ로 전달되고 escapeStringConn 및 친구들에서 새로운 일반 문자로 교체되어 더 많은 가비지가 추가됩니다. 텍스트를 다른 Int로 바꾸면 GC 시간이 75 %에서 45 %로 떨어집니다.

formatMany 및 buildQuery에 의한 임시 목록 사용을 줄이기 위해 builM의 foldM으로 mapM을 대체하려고했습니다. 많은 도움이되지는 않지만 코드 복잡성은 약간 증가합니다.

TLDR - Builders은 모두 최종 엄격한 ByteString (많은 바이트 배열)을 생성하는 데 필요하므로 느리게 사용할 수 없습니다. 메모리에 문제가 있으면 동일한 트랜잭션 내에서 executeMany를 청크로 분할하십시오.

+0

이 문제는 직접 해결했지만 너무 명확하지는 않습니다.빌더는 게으른 ByteString도 생성 할 수 있지만, libPQ는 불행히도 게으른 문자열을 소비하지 않습니다. 이것은 haskell의 ByteStrings에서 정말 바보 같은 문제입니다. – s9gf4ult

관련 문제