본문 바로가기
Engineer/코드최적화

2. 효율적인 반복문(loop) 작성

by _제이빈_ 2020. 3. 12.

본 게시글은 원본1원본2원본3 를 따라해 가며 이해한 내용들을 기록한 문서이다.

코드 최적화 관련 글 목록

 

 과학계산에서의 코드 최적화

 

    Fortran 및 C는 과학계산용 프로그램을 작성할때 자주 사용되는 언어이다. 계산속도를 높이기 위해서는 데이터의 저장방법이나, 반복계산 등의 세부 사항을 이해하고 최적화 시켜야한다. 몇 가지 일반적인 효율성 문제를 해결할수 있지만, Donald E. Knuth (1974)의 말을 항상 명심해야 한다. 프로그램을 최적화도 적기에 해야한다는 말이다. 섣불리 했다가 오히려 시간만 잡아먹을 수 있다고 이해할 수 있겠다.

 

 

"We should forget about small efficiencies, say about 97% of the time:

premature optimization is the root of all evil".

 

 

그러니, 최적화를 수행하기 이전에 프로파일링을 통한 주요 계산 부분을 담당하는 모듈을 식별해 내는 것이 필요하다. 그리고 반복문을 분석하여 최적화를 저하시키는 요인을 솎아 내야한다.  다른 교과서를 참고하여 자세한 사항을 알면 좋을 것이고, 본 게시글에서는 3가지 경우에 대해서만 다룰 것이다.

 

    "최적화를 저하시키는 요인"이라는 단어를 썼을때 느낄 수 있었겠지만, 대부분의 상황에서 컴파일러에 포함된 최적화 라이브러리를 사용하면 수동으로 최적화 코딩하는 것보다 좋은 성능을 나타낸다. 예를 들면, Sun Performance Library는 표준 LAPACK, BLAS, FFTPACK, VFFTPACK 및 LINPACK 라이브러리를 기반으로 고도로 최적화 된 수학 서브 루틴 제품으로 이를 사용하면 된다. 이를 이용한 성능 개선은 수동 최적화 코딩과 비교해 더 좋은 퍼포먼스를 내는 경우가 많다. 이런 사실에 빗대어 생각해 보면, 비전공자로써 반복문을 최적화하는 방법은 핸드코딩이 아니라, 컴파일러가 최적화를 잘~수행할 수 있도록 컴파일러의 리즈에 맞춰주는 것다. 

 

■ 컴파일러의 최적화를 극대화하기 위한 준비

 

     최적화 성능을 억제시키는, 특히 반복문과 관련된 사항들에 대해 다뤄보도록 한다. 반복문은 특히 계산 집약적일때 최적화가 용이하므로, 기본 원리는 반복문 내 수치계산만 남기는 것이다. 이를 위해 (1) 주요 반복문에서 I/O를 제거하고, (2) 서브루틴 호출도 제거하고,  (3) 조건문(if)을 블록화시켜 최적화 할 수 있다. 차례대로 알아보자.

 

 

1 주요 반복문에서 I/O를 제거

 

프로그램내 주요 계산 작업을 수행하는 반복문에 I/O (input/output)가 포함되어 있을 경우 성능이 저하된다. I/O를 위해 CPU 시간이 소요되어 배보다 배꼽이 더 커지는 경우가 발생한다. I/O는 프로세스를 방해(Process interrupts)해 처리량을 저하시킨다. 가능한 계산 루프 밖으로 I/O를 이동시켜 I/O 라이브러리 호출를 크게 줄여야 한다.

 

 

서브루틴 호출도 제거

 

반복문 내에 서브루틴을 호출하는 경우 수백-수천번 이상 호출 되기 마련이다. 각 호출에 소요되는 시간이 적다한들, 반복문이 모두 수행되고 나면 소요시간이 커질 수 있다. 또한, 서브루틴을 호출 시 컴파일러가 반복문을 최적화하지 못하게 된다(컴파일러는 호출에 대한 레지스터 상태를 가정 할 수 없기 때문이다.) 이를 대체하기 위한 방법을 3가지 제시하고 있다.

 

첫째로 서브 프로그램 호출의 자동 인라인(-inline = x, y, .. z 또는 -O4 사용)을 사용하는 것이다(즉, 서브 프로그램을 반복문 내로 풀링). 이 경우 인라인 될 서브루틴의 소스 코드는 호출 루틴과 동일한 파일 내에 있어야 한다.

 

두번째는, 명령문 함수를 사용하는 것이다. 호출되는 외부 함수가 간단한 수학 계산이라면, 명령문 함수들로 다시 작성할 수 있을 것이다. 이런 명령문 함수는 인라인으로 컴파일되어 최적화 될 수 있다.

 

      REAL C,F,TEMP
      C(F) = 5.0*(F - 32.0)/9.0
      WRITE(*,*)'Enter the temperature in Fahrenheit'
      READ(*,*)TEMP
      WRITE(*,*)'Fahrenheit = ',TEMP,' and Celsius = ',C(TEMP)

 

셋째로는 서브루틴에 반복문을 넣는 것이다. 즉, 불가피한 반복문을 서브루틴에 넣어 대신 함수호출을 적게 하는 것이다. 그리고 호출 시 벡터 또는 값의 배열에서 작동하도록 서브 프로그램을 다시 작성하면 되겠다.

 

 

조건문(if)을 블록화

 

루프 내에서 복잡한 조건부 연산 또한  컴파일러의 최적화를 방해할 수 있다. 이를 위해 산술(arithmetic) 및 논리(logical) IF를 제거하고, 블록 IF로 대체하는 것이 일반적다.

 

 

 

 

포트란을 위주로 컴파일시 최적화가 원활히 수행될 수 있도록 하는 몇가지 조건들에 대해서 알아보았다. 기본적인 내용이라, 다른 언어들에게도 통용될거란 생각이든다. 코드를 먼저 위와같이 최적화 시키고, 컴파일러마다 지원하는 최적화 옵션들을 공부하고, 적절히 사용하도록 해야겠다. 

반응형

'Engineer > 코드최적화' 카테고리의 다른 글

3. 인텔포트란 최적화 옵션 정리  (0) 2020.03.12
1. 효율적인 배열데이터 참조  (0) 2020.03.12
코드 최적화  (0) 2020.03.12

댓글