2011-05-08 5 views
10

몇 가지 실험을 해본 결과 여기에 제가 발견 한 것이 있습니다.하스켈 - for 루프와 동등한 성능을 제공합니까?

int main() 
{ 
    for(int i = 0; 
     i < 1000000; 
     ++i) 
    {} 
} 

다음 하스켈 프로그램 : 다음 C 프로그램 고려 여기

import System.IO 

loop :: Int -> IO() 
loop n = if 0 == n then return() else loop (n-1) 

main = loop 1000000 

은 상기 C 프로그램 time의 출력이다

real 0m0.003s 
user 0m0.000s 
sys 0m0.000s 

... 및 대 Haskell 프로그램 :

real 0m0.028s 
user 0m0.027s 
sys 0m0.000s 

처음에는 gcc가 빈 루프를 감지하고이를 최적화했지만 반복 횟수를 늘린 후 프로그램 실행 시간이 증가했다고 생각했습니다.

C 버전

real 0m0.024s 
user 0m0.023s 
sys 0m0.000s 

하스켈 버전

real 0m0.245s 
user 0m0.247s 
sys 0m0.000s 

당신이 볼 수 있듯이, 하스켈 다음은 프로그램의 모두 time의 출력은 10000000로 설정 반복의 수입니다 프로그램은 10 배 느립니다.

질문 : Haskell의 for 루프에 대한 효율적인 대안은 무엇입니까? 방금 본 것처럼 간단한 재귀는 프로그램을 약 10 배 정도 느려지 게합니다 (그리고 이것은 꼬리 재귀 최적화를 수행 한 것일 수 있습니다).

+1

를 확인, 완전히 최적화 된 C 프로그램에서 생성 된 어셈블리 코드는 루프를 제거 하는가? – shuttle87

+0

Pfft, 'while'루프. 이봐. : P –

+0

@ shuttle87 -O3 플래그를 사용하면 루프가 완전히 제거되고 루프는 제거되지 않습니다. –

답변

22

을 첫째로 당신이 당신의 C 코드를 번역하는 것 :

그리고 리 Javadyan, 나는이 버전 -O2에서 감지됩니다 기대 원래 버전은 -03에서 밖으로 최적화 된 가져옵니다 코멘트에 말했다 이것에 대해

main = go 0 
    where 
     go :: Int -> IO() 
     go i | i < 1000000 = go (i+1) 
      | otherwise = return() 

ghc는 빈 프로그램에 최적화됩니다. 그것은 레지스터에 최종 값을 이동 반대 비교하고 ()를 반환

Main_zdwa_info: 
    cmpq $1000000, %r14   # imm = 0xF4240 
    movl $1000000, %eax   # imm = 0xF4240 
    cmovlq %rax, %r14 
    movq (%rbp), %rax 
    movl $ghczmprim_GHCziUnit_Z0T_closure+1, %ebx 
    jmpq *%rax # TAILCALL 

하는 실행하면

$ time ./A 
./A 0.00s user 0.00s system 88% cpu 0.004 total 

더 시간이 필요하지 않습니다. 일반적으로


는하지만, GHC는 꼬리 재귀 기능, equivalent loops to e.g. GCC 방출한다. 특히 최상의 결과를 얻으려면 숫자 벤치 마크를 ghc -O2 -fllvm으로 컴파일해야합니다. 프로그램을 최적화하지 않으려면 ghc가 지정한 프로그램을 기꺼이 실행하게됩니다.이 경우 더 높은 최적화 수준에서 제거되는 많은 중복 작업이 필요합니다.하스켈 프로그램의 낮은 수준의 성능을 분석에 대한 자세한 내용은

, 호기심에서 RWH ch25.

+0

나쁜 코드처럼 보입니다. 중복 이동이 있습니다. 내 말은, 루프를 모두 제거하거나 유지하는 것입니다. 그것의 3 가지 지침을 저장하지 마십시오. – augustss

+0

메인은 여전히 ​​IO()를 반환해야합니다. – muhmuhten

6

루핑 구문의 경우 외부 함수 수준에서 재귀가 아닌 GHC 스팟 최적화를 돕기 위해 종종 Worker/Wrapper 스타일로 작성해야합니다. ,

import System.IO 

loop :: Int -> IO() 
loop n = go n 
    where 
    go n | n <= 0 = return() 
    go n   = go (n-1) 

main = loop 1000000