2012-05-24 2 views
3

이 프로그램은 해시 함수 충돌을 찾기 위해 매우 큰 세트를 만듭니다. GC에서 소비되는 시간을 줄이는 방법이 있습니까? + RTS -s는 GC에서 소비 된 40 % + 시간을보고합니다.큰 세트 만들기 - GC에서 소비하는 시간을 줄여야합니다.

사용 예제 :

./program 0 1000000 +RTS -s 
./program 145168473 10200000 +RTS -s 

내가 사용할 수있는 더 나은 알고리즘 또는 데이터 구조가 있습니까?

{-# LANGUAGE OverloadedStrings #-} 

import System.Environment 
import Control.Monad 
import Crypto.Hash.SHA256 

import qualified Data.ByteString.Char8 as B 
import qualified Data.ByteString.Lazy.Char8 as BL 
import Data.Char 
import Data.Int 
import Data.Bits 
import Data.Binary 
import Data.Set as Set 
import Data.List 
import Numeric 

str2int :: (Integral a) => B.ByteString -> a 
str2int bs = B.foldl (\a w -> (a * 256)+(fromIntegral $ ord w)) 0 bs 

t50 :: Int64 -> Int64 
t50 i = let h = hash $ B.concat $ BL.toChunks $ encode i 
     in 
      (str2int $ B.drop 25 h) .&. 0x3ffffffffffff 

sha256 :: Int64 -> B.ByteString 
sha256 i = hash $ B.concat $ BL.toChunks $ encode i 

-- firstCollision :: Ord b => (a -> b) -> [a] -> Maybe a 
firstCollision f xs = go f Set.empty xs 
    where 
    -- go :: Ord b => (a -> b) -> Set b -> [a] -> Maybe a 
    go _ _ []  = Nothing 
    go f s (x:xs) = let y = f x 
        in 
         if y `Set.member` s 
         then Just x 
         else go f (Set.insert y s) xs 

showHex2 i 
    | i < 16 = "0" ++ (showHex i "") 
    | otherwise = showHex i "" 

prettyPrint :: B.ByteString -> String 
prettyPrint = concat . (Data.List.map showHex2) . (Data.List.map ord) . B.unpack 


showhash inp = 
    let h = sha256 inp 
     x = B.concat $ BL.toChunks $ encode inp 
    in do putStrLn $ " - input: " ++ (prettyPrint x) ++ " -- " ++ (show inp) 
     putStrLn $ " - hash: " ++ (prettyPrint h) 

main = do 
     args <- getArgs 
     let a = (read $ args !! 0) :: Int64 
      b = (read $ args !! 1) :: Int64 
      c = firstCollision t [a..(a+b)] 
      t = t50 
     case c of 
      Nothing -> putStrLn "No collision found" 
      Just x -> do let h = t x 
         putStrLn $ "Found collision at " ++ (show x) 
         showhash x 
         let first = find (\x -> (t x) == h) [a..(a+b)] 
          in case first of 
           Nothing -> putStrLn "oops -- failed to find hash" 
           Just x0 -> do putStrLn $ "first instance at " ++ (show x0) 
              showhash x0 
+3

힙 프로파일 링을 사용하여 진행 상황을 파악합니다. – augustss

+1

예를 들어 할당 영역을 늘리려고 했습니까? 100MB의'-RTS -A100M'은? 이는 GC 실행 빈도를 줄이면 도움이되는 경우가 있습니다. 이런 식으로, 대부분의 생성 된 데이터가 잠시 보관되는 곳에서는 큰 차이를 만들 수 있습니다. –

+0

's'는'firstCollision'의 작업자에서 엄격해야합니다. – luqui

답변

2

한 가지가 binary 패키지의 사용을 통해 ByteString의를 건설하고 (당신이에/게으른 덩어리에서이를 방지하려는 경우이 방법으로 cereal을 사용할 수 있습니다). 그들이 사용하는 Builder 모나드 내부를 들여다 보면 기본 초기 크기가 약 32k임을 알 수 있습니다. 귀하의 목적에 따라 아마도 이것은 가비지 컬렉터에 필요한 것보다 더 많은 압력을 가하고 있습니다. 단지 8 바이트가 필요하다는 것을 고려하면됩니다.당신이 정말로 단지 인코딩 binary을 사용하고 있기 때문에

, 당신은 같은 그것을 스스로 할 단지 수 :

encodeInt64 :: Int64 -> B.ByteString 
encodeInt64 x = 
    let 
    go :: Int -> Maybe (Word8, Int) 
    go i 
     | i < 0  = Nothing 
     | otherwise = 
     let 
      w :: Word8 
      w = fromIntegral (x `shiftR` i) 
     in Just (w, i-8) 
    in fst $ B.unfoldrN 8 go 56 

난 당신이 버퍼에 직접 바이트를 파고 더 나은 아마 이렇게도 할 수있는 위험을 무릅 것 .

은 다른 비 GC 관련 포인트는 당신이 unordered-containers에서 Data.HashSet와 약간 나은 성능을 찾을 수있는 표준 Data.Set 구현을 사용하고 있다는 것입니다, 한 가지입니다.

Don이 언급 한 마지막 요점은 -A200M (또는 there-abouts)으로 더 큰 할당 영역을 요청할 수 있다는 것입니다. (Data.HashSet를 사용하여 자신의 인코더 및 -A200M) 위의 모든 수정 사항과 함께

은, 코드의 실행은 각각 52.9 %와 21.2 %의 %의 GC 시간으로, 7.397s에서 내 컴퓨터에 3.474s로 이동합니다.

Big-O의 감각으로는 잘못된 것이 없지만 조금 떨어 뜨릴 수있는 상수가 있습니다!

1

잘 모르겠습니다. 난 당신이 firstCollision에 썽크를 구축 있다고 생각

heap profile

을 (+RTS -hT으로 실행) 여기

힙 프로파일 :하지만, 여기 경우 누군가의 일부 프로파일 출력이 그것에서 진짜 대답을 구성 할 수있어 uncomced 평가로 인해 Set.insert. 그러나 메모리 할당량은 절대적인 측면에서 너무 적어서 실제 범인이라고 확신하지 못합니다. 아래를보십시오.

여기 (-prof -fprof-auto 컴파일 +RTS -p으로 실행) 프로파일 러의 출력입니다 : 기본적으로

COST CENTRE   MODULE %time %alloc 

firstCollision.go Main  49.4 2.2 
t50.h    Main  39.5 97.5 
str2int    Main  5.4 0.0 
firstCollision.go.y Main  3.4 0.0 
t50     Main  1.1 0.0 

메모리 할당의 모든 것 같다 파이프 라인 sha256를 해싱/일련의 순차적 인의 현지 통화로 이에 상응하는 금액 h에서오고있다 계속되는 중간 데이터 구조 구축이 필요합니다.

는 노련한 사람들은 더 정확하게 문제를 정확하게 파악 할 수 있습니까? 당신이 발견으로

+2

평가되지 않은 썽크가 있다면, 그들은 'THUNK'타입으로 나타나야합니다. 이 메모리 프로파일은 적어도이 알고리즘에있어서 나에게 꽤 좋아 보인다. –

4

는 GC 통계는 낮은 생산성을보고 :

44,184,375,988 bytes allocated in the heap 
    1,244,120,552 bytes copied during GC 
     39,315,612 bytes maximum residency (42 sample(s)) 
     545,688 bytes maximum slop 
      109 MB total memory in use (0 MB lost due to fragmentation) 

            Tot time (elapsed) Avg pause Max pause 
    Gen 0  81400 colls,  0 par 2.47s 2.40s  0.0000s 0.0003s 
    Gen 1  42 colls,  0 par 1.06s 1.08s  0.0258s 0.1203s 

    INIT time 0.00s ( 0.00s elapsed) 
    MUT  time 4.58s ( 4.63s elapsed) 
    GC  time 3.53s ( 3.48s elapsed) 
    EXIT time 0.00s ( 0.00s elapsed) 
    Total time 8.11s ( 8.11s elapsed) 

    %GC  time  43.5% (42.9% elapsed) 

    Alloc rate 9,651,194,755 bytes per MUT second 

    Productivity 56.5% of total user, 56.4% of total elapsed 

가장 눈에 띄는 첫 번째 단계는 크기 조정에 대한 필요성을 제거하려고하는 GC의 기본 영역을 증가시키는 것이다. 예를 들어, is to increase the -A area ( ) 도구 like GC tune을 사용하면 프로그램에 맞는 설정을 찾을 수 있습니다.

$ ./A ... +RTS -s -A200M 

    Total time 7.89s ( 7.87s elapsed) 

    %GC  time  26.1% (26.5% elapsed) 

    Alloc rate 7,581,233,460 bytes per MUT second 

    Productivity 73.9% of total user, 74.1% of total elapsed 

그래서 우리는 25 분의 1 초를 줄 였지만 생산성은 75 %로 향상되었습니다. 이제 우리는 힙 프로파일 보라 : 설정 및 Int 인 값의 선형 성장을 보여주는

enter image description here

. 이것은 알고리즘이 지정하는 것입니다. 따라서 모든 결과를 유지하는 한 많이 볼 수는 없습니다. 당신이 많이하고있는

관련 문제