• 검색 결과가 없습니다.

4. 순차코드 최적화

4.5. 프로세서, I/O, 라이브러리 관련 최적화

4.5.2. 부동소수 명령어

과학계산 프로그램은 대부분 부동소수 연산으로 구성되므로 프로세서의 부동소수 연산 처리 능력 은 프로그램의 성능을 결정하는데 아주 중요한 역할을 한다. 부동소수 처리 성능을 높이기 위해 최신 프로세서들의 FPU(Floating-Point Unit) 디자인에 많은 특성이 추가되고 있다.

IEEE 754는 대부분의 최신 아키텍처들이 따르고 있는 부동소수 처리에 대한 표준이다. 보다 최적

화된 코드를 만들기 위해 IEEE 754에 대한 이해가 도움이 될 것이다.

4.5.2.1. Combined Multiply/Add Instructions: MADD, FMA

모든 프로세서 아키텍처는 프로세서 코어에 적어도 하나 이상의 FPU를 가진다. 이때 FPU는 하 나로 곱셈과 덧셈을 모두 처리할 수 있는 것도 있고, 각각 덧셈과 곱셈을 처리하는 분리된 형태의 FPU로 있을 수도 있다.

IBM POWER4와 Itanium Family와 같은 일부 최신 아키텍처에서는 덧셈과 곱셈을 하나의 명령어 로 한꺼번에 실행할 수 있는 FPU를 가진다. 한꺼번에 처리되는 덧셈/곱셈 패턴은 많은 과학계산 알고리즘의 기본이 되는 선형대수 계산에서 매우 흔하기 때문에 이를 지원하는 아키텍처는 그렇 지 않은 아키텍처에 비해 현저한 성능 증가를 보인다.

MADD 연산 처리가 가능하고, 8개의 부동소수 레지스터가 있는 FPU 하나를 가지는 가상의 프로 세서에서 다음과 같은 코드를 실행하는 경우를 살펴보자.

INTEGER I,N

PARAMETER (N=1000) REAL X(N), A(N), B(N), C(N) ...

DO I=1,N

X(I)=A(I)*B(I)+C(I) END DO

MADD 명령어를 사용하지 않는다면 컴파일러는 위 코드에 대해 아래와 유사한 명령어 흐름을 발 생시킬 것이다.

top: LD &a+i,rf1 # load a(i) into FP register 1 LD &b+i,rf2 # load b(i) into FP register 2

FMU rf1,rf2,rf3 # multiply the contents of FP registers 1 & 2

# and store the result in FP register 3 LD &c+i,rf4 # load c(i) into FP register 4

FAD rf3,rf4,rf5 # add the contents of FP registers 3 & 4

# and store the result in FP register 5 ST rf5,&x+I # store FP register 5 into x(i)

INC i,1 # increment i by 1 BLT i,n,top # branch to top if i<n

한 번의 반복 계산 동안 10개의 명령어가 실행되고, 5개의 레지스터를 사용하고 있다. 그러나,

MADD 명령어를 사용한다면 다음과 같이 명령어 흐름이 간단해 진다. 루프 반복당 실행되는 명 령어 개수가 9개, 사용되는 레지스터 개수도 4개가 돼 하나씩 줄어 들었다.

top: LD &a+i,rf1 # load a(i) into FP register 1 LD &b+i,rf2 # load b(i) into FP register 2 LD &c+i,rf3 # load c(i) into FP register 3

FMA rf1,rf2,rf3,rf4 # multiply the contents of FP registers 1

# and 2, add the contents of FP register 3,

# and store the result in FP register 4 ST rf4,&x+i # store FP register 5 into x(i)

INC i,1 # increment i by 1 BLT i,n,top # branch to top if i<n

MADD 명령어를 지원하는 프로세서 아키텍처에서는 기본적으로 이 명령어를 사용한다. 만약 필 요에 의해 MADD 명령어 사용을 막아야 한다면, 해당 아키텍처를 지원하는 컴파일러에서 제공하 는 컴파일러 옵션을 이용해 MADD 명령어 사용을 금하도록 할 수 있다.

4.5.2.2. Division

나눗셈(division)은 덧셈이나 곱셈보다 실행시간이 훨씬 더 걸린다. 덧셈 또는 곱셈은 파이프라인 으로 처리 가능하고 연산이 완료되기까지 보통 5~10 클럭 사이클이 소비되지만, 나눗셈은 파이프 라인으로 처리 되지 않으며, 연산이 완료되기까지 20~60 사이클 정도가 소비 된다. 이러한 이유 로 다음과 같이 루프 nest에서 inner 루프에 나눗셈이 들어가는 경우는 가급적 피하는 것이 좋다.

INTEGER I,N

PARAMETER (N=1000) REAL X(N), A(N), B DO I=1,N

X(I)=A(I)/B END DO

성능이 좋지 않은 나눗셈을 루프 nest에서 제거하기 위해 다음과 같이 역수를 이용한 변환이 가 능하다.

INTEGER I,N

PARAMETER (N=1000) REAL X(N), A(N), B, RB

RB = 1./B DO I=1,N X(I)=A(I)*RB END DO

이와 같은 변환이 컴파일러 수준에서 자동 처리되면 편리하겠으나, 부동소수 처리에 대한 IEEE 754 표준에서는 정밀도 손실 문제로 인해 컴파일러에 의한 자동 변환을 금지하고 있다. 그러나, 대부분의 컴파일러에서는 이와 같은 변환을 컴파일 단계에서 수행하도록 하는 옵션을 따로 제공 하고 있다.