2012-03-14 2 views
8

함수를 별도의 모듈로 옮기면 프로그램 성능이 크게 떨어집니다. 다른 모듈함수가 모듈간에 이동하는 경우 성능이 크게 달라집니다.

calc = sum . nub . map third . filter isProd . concat . map parts . permutations 
    where third (_,_,b)   = fromDigits b 
      isProd (a,b,p)   = fromDigits a * fromDigits b == fromDigits p 
      -- All possibilities have digits: A x AAAA or AA x AAA 
      parts (a:b:c:d:e:rest) = [([a], [b,c,d,e], rest) 
            ,([a,b], [c,d,e], rest)] 

:

fromDigits :: Integral a => [a] -> a         
fromDigits = foldl1' (\a b -> 10 * a + b) 

fromDigits은 동일 모듈에있을 때 0.1 초에서 실행하지만, 0.4 초 I는 다른 모듈로 이동합니다.

GHC가 다른 모듈에있는 경우 GHC가 함수를 인라인 할 수 없기 때문에 이것을 가정합니다.하지만 동일한 패키지에 있기 때문에 이어야합니다.이 가능해야합니다.

저는 컴파일러 설정이 무엇인지 모르지만 Leksah/cabal 기본값으로 작성되었습니다. 나는 그것이 적어도 -O2와 최소한 같다고 확신한다.

+1

GHC는 모듈을 통해 인라인 할 수 있습니다. 다음을 참고하십시오 : http://www.haskell.org/haskellwiki/Performance/GHC#Modules_and_separate_compilation – porges

+1

'fromDigits' 정의 근처에'{- # INLINE fromDigits # -}'주석을 추가 할 수 있습니다. – hvr

+0

감사합니다. INLINE pragma가 작동합니다. 하지만 .. 통화 사이트에서이를 지정할 수 없다는 것이 약간 이상하다고 생각합니다. –

답변

8

의 펼쳐지는 자동 노출. 이는 호출 사이트에서 전문화 될 수 없으며 사전 조회가 인라인 추가 및 곱셈 (결과적으로 더 많은 최적화를 가능하게 할 수 있음)으로 제거 될 수 없음을 의미합니다.

GHC는 최적화와 함께 사용되는 동일한 모듈에서 정의 될 때, 사용 된 유형에 대한 특수 버전을 생성합니다 (알려진 경우). 그런 다음 사전 조회를 제거하고 (+)(*) 연산을 인라인 할 수 있습니다 (사용되는 유형에 인라인에 적합한 연산이있는 경우).

그러나 알려진 유형에 따라 다릅니다. 따라서 하나의 모듈에 다형 calcfromDigits이 있지만 다른 모듈에서만 사용하는 경우 일반 버전 만 사용할 수있는 위치에 있지만 펼쳐지지 않으므로 특수화 할 수 없습니다 또는 다른 방법으로 호출 사이트에서 최적화 할 수 있습니다.

하나의 해결책은 인터페이스 파일에 노출 된 함수의 펼침을 만드는 것입니다. 따라서 필요한 데이터 (특히 유형)를 사용할 수있을 때 함수가 사용되는 곳에서 적절하게 최적화 할 수 있습니다. {-# INLINE #-}을 추가하거나 GHC 7에서 {-# INLINABLE #-} pragma를 추가하여 인터페이스 파일에서 함수의 전개를 폭로 할 수 있습니다. 따라서 호출 코드를 컴파일 할 때 거의 변경되지 않은 소스 코드를 사용할 수 있으므로 함수를 더 많은 정보를 사용하여 적절하게 최적화 할 수 있습니다.

코드 단점은 모든 호출 사이트에서 최적화 된 코드 사본을 얻을 수 있다는 것입니다 (INLINABLE은 극단적이지 않으므로 호출 모듈 당 적어도 하나의 복사본을 얻습니다. 보통 그렇게 나쁘지는 않습니다).

대체 솔루션 GHC는 중요한 종류에 최적화 된 버전을 만들 수 있도록 {-# SPECIALISE #-} 프라 그마 (도 받아 미국 철자)를 추가하여 정의 모듈 전문 버전을 생성하는 것입니다 (Int, Integer, Word을?). 또한 다시 쓰기 규칙을 작성하므로 특수화 유형의 사용은 특수 버전을 사용하도록 다시 작성됩니다 (최적화를 사용하여 컴파일 할 때).

이 단점은 코드가 인라인 될 때 ​​가능할 수있는 최적화가 아니라는 것입니다.

+2

제 이해는'{- # INLINABLE # -}'로 전문 분야를 공유 할 수 있기 때문에 모든 호출 사이트에서 사본만큼 나쁘지는 않습니다. – Anthony

+0

참. 추후 고려 사항으로 코드 확장 주석을 업데이트하는 것을 잊었습니다. 그 점을 알려 주셔서 감사합니다. –

5

inline 기능을 사용하여 GHC에 전화 사이트에서 기능을 인라인 할 수 있습니다. http://www.haskell.org/ghc/docs/7.0.4/html/libraries/ghc-prim-0.2.0.0/GHC-Prim.html#v%3Ainline. 당신은 INLINABLE 프라 그마와 함께 사용 할 수 있습니다 : 당신이 때문에 (+), (*)fromInteger에 대한 사전 조회에있는 기능을 얻을, 너무 큰 가지고 유형 수준의 다형성 fromDigits를 들어 http://www.haskell.org/ghc/docs/7.0.4/html/users_guide/pragmas.html#inlinable-pragma

관련 문제