MIPS 부동소수점 연산자
- 부동소수점 하드웨어는 보조 프로세서(coprocessor) 1번
- ISA를 확장하는 보조 프로세서
- 메인 메모리의 프로세스는 인덱스가 32비트*32개가 있었다.
- coprocessor도 인덱스가 32비트*32개가 있다.
- f로 시작 : $f0 ~ $f31번 까지
- 별도의 FP 레지스터
- 32 single-precision : $f0, $f1, ..., $f31
- double-precision 쌍 : $f0/$f1, $f2/$f3, ...
- MIP 릴리스 2 ISA는 32*64비트 부동소수점 레지스터을 지원한다.
- double의 경우 짝수번만 사용한다.
- 부동소수점 명령어는 부동소수점 레지스터에서만 작동한다.
- 프로그램은 일반적으로 부동소수점 데이터에 대해 정수 연산을 수행하지 않으며 그 반대의 경우도 마찬가지이다.
- add $f0, $f2 등이 사용 불가
- 코드 크기에 미치는 영향을 최소화하면서 더 많은 레지스터
- 프로그램은 일반적으로 부동소수점 데이터에 대해 정수 연산을 수행하지 않으며 그 반대의 경우도 마찬가지이다.
- 부동소수점 로드 및 저장 명령어
- lwc1, ldc1, swc1, sdc1
- load word coprocess, load double coprocess..
- 예: ldc1 $f8, 32($sp)
- lwc1, ldc1, swc1, sdc1
- Single-precision 산술
add.s, sub.s, mul.s, div.s
- 예제
add.s $f0, $f1, $f6
- Double-precision 산술
add.d, sub.d, mul.d, div.d
- 예제
mul.d $f4, $f4, $f6
- Single-precision과 Double-precision 비교
c.xx.s, c.xx.d(xx는 eq, lt, le, …)
부동소수점 조건 코드 비트를 설정하거나 지운다.
- 예제
c.lt.s $f3, $f4
- FP 조건 코드 참 또는 거짓 분기
bc1t, bc1f
- 예제
bc1t TargetLabel
부동소수점 예: °F to °C
- C 코드
float f2c (float fahr) {
return ((5.0/9.0)*(fahr - 32.0));
}
- fahr in $f12, result in $f0, literals in global memory space
- MIPS 코드
f2c: lwc1 $f16, const5($gp)
lwc2 $f18, const9($gp)
div.s $f16, $f16, $f18
lwc1 $f18, const32($gp)
sub.s $f18, $f12, $f18
mul.s $f0, $f16, $f18
jr $ra
부동소수점 예: 배열 곱셈
- X = X + Y × Z
- 모든 32 × 32 행렬, 64비트 double-precision 요소
- C 코드
void mm (double x[][], double y[][], double z[][]) {
int i, j, k;
for (i = 0; i! = 32; i = i + 1)
for (j = 0; j! = 32; j = j + 1)
for (k = 0; k! = 32; k = k + 1)
x[i][j] = x[i][j] + y[i][k] * z[k][j];
}
- x 주소 : $a0
- y 주소 : $a1
- z 주소 : $a2
- i : $s0
- j : $s1
- k : $s2
- MIPS 코드
li $t1, 32 # $t1 = 32 (row size/loop end)
li $s0, 0 # i = 0; initialize 1st for loop
L1: li $s1, 0 # j = 0; restart 2nd for loop
L2: li $s2, 0 # k = 0; restart 3rd for loop
sll $t2, $s0, 5 # $t2 = i * 32 (size of row of x)
addu $t2, $t2, $s1 # $t2 = i * size(row) + j
sll $t2, $t2, 3 # $t2 = byte offset of [i][j]
addu $t2, $a0, $t2 # $t2 = byte address of x[i][j]
l.d $f4, 0($t2) # $f4 = 8 bytes of x[i][j]
L3: sll $t0, $s2, 5 # $t0 = k * 32 (size of row of z)
addu $t0, $t0, $s1 # $t0 = k * size(row) + j
sll $t0, $t0, 3 # $t0 = byte offset of [k][j]
addu $t0, $a2, $t0 # $t0 = byte address of z[k][j]
l.d $f16, 0($t0) # $f16 = 8 bytes of z[k][j]
sll $t0, $s0, 5 # $t0 = i*32 (size of row of y)
addu $t0, $t0, $s2 # $t0 = i*size(row) + k
sll $t0, $t0, 3 # $t0 = byte offset of [i][k]
addu $t0, $a1, $t0 # $t0 = byte address of y[i][k]
l.d $f18, 0($t0) # $f18 = 8 bytes of y[i][k]
mul.d $f16, $f18, $f16 # $f16 = y[i][k] * z[k][j]
add.d $f4, $f4, $f16 # f4=x[i][j] + y[i][k]*z[k][j]
addiu $s2, $s2, 1 # $k k + 1
bne $s2, $t1, L3 # if (k != 32) go to L3
s.d $f4, 0($t2) # x[i][j] = $f4
addiu $s1, $s1, 1 # $j = j + 1
bne $s1, $t1, L2 # if (j != 32) go to L2
addiu $s0, $s0, 1 # $i = i + 1
bne $s0, $t1, L1 # if (i != 32) go to L1
정확한 산술
- IEEE Std 754는 추가 반올림 제어를 지정한다.
- 추가 정밀도(guard, round, sticky)
- 반올림 모드 선택
- 프로그래머가 계산의 수치적 동작을 미세 조정할 수 있다.
- 모든 부동소수점유닛(FPU)이 모든 옵션을 구현하는 것은 아니다.
- 대부분의 프로그래밍 언어와 부동소수점 라이브러리는 기본값만 사용합니다.
- 하드웨어 복잡성, 성능 및 시장 요구 사항 간의 균형
Guard 숫자 반올림
2.56 * 10^0 + 2.34 * 10^2
- 유효 소수점 이하 자릿수가 3개 있다 가정하면
- 지수를 정렬하려면 작은 숫자를 오른쪽으로 이동(2.56 x 10^0 → 0.0256 x 10^2)
- 0.0256에서 56이 잘리게 된다.
- Guard 비트와 round 비트를 사용하여 두 개의 LSD를 표현할 수 있다.
- Guard 비트와 round 비트로 저장한 값을 통해 반올림을 통해 더 가까운 값을 찾을 수 있다.
2.3400 + 0.0256 = 2.3656 ≒ 2.37
vs.
2.34 + 0.02 = 2.36
- sticky 비트
- 하위비트가 있는지 없는지 유무를 저장
- 표준에는 guard와 round 외에 세 번째 비트가 있다.
- round 비트 오른쪽에 0이 아닌 비트가 있을 때마다 설정된다.
- 이 sticky 비트를 통해 컴퓨터는 반올림 시 0.50 ~ 00과 0.50 ~ 01 사이의 차이를 확인할 수 있다.
하위 단어(Subword) 병렬성
- 그래픽 및 오디오 애플리케이션은 짧은 벡터에 대한 동시 작업 수행을 활용할 수 있다.
- 하나의 가산기로 여러 개 동시 연산이 가능하다.
- 예: 128비트 가산기
- 16개의 8비트 추가
- 8개의 16비트 추가
- 4개의 32비트 추가
- 데이터 수준 병렬성, 벡터 병렬성, SIMD(Single Instruction, Multiple Data)라고도 한다.
x86 부동소수점 아키텍처
- 원래 8087 부종소수점 보조 프로세서를 기반으로 한다.
- 8 × 80비트 확장 정밀도 레지스터
- 푸시다운 스택으로 사용
- TOS에서 인덱싱된 레지스터: ST(0), ST(1), ...
- 부동소수점 값은 메모리에서 32비트 또는 64비트
- 메모리 피연산자의 로드/저장 시 변환된다.
- 정수 피연산자도 로드/저장 시 변환될 수도 있다.
- 코드 생성 및 최적화가 매우 어렵다.
- 결과: 부동소수점 성능 저하
데이터 전송 | 산수 | 비교 | 초월수 |
FILD mem/ST(i) FISTP mem/ST(i) FLDPI FLD1 FLDZ |
FIADDP mem/ST(i) FISUBRP mem/ST(i) FIMULP mem/ST(i) FIDIVRP mem/ST(i) FSQRT FABS FRNDINT |
FICOMP FIUCOMP FSTSW AX/mem |
FPATAN F2XMI FCOS FPTAN FPREM FPSIN FYL2X |
- 선택적 변형
- I: 정수 피연산자
- P: 스택에서 피연산자 pop
- R: 피연산자의 역순
- 하지만 모든 조합이 허용되는 것은 아니다.
Streaming SIMD Extension 2 (SSE2)
- 4 * 128비트 레지스터 추가
- AMD64/EM64T에서 8개 레지스터로 확장
- 여러 부동소수점 피연산자에 사용할 수 있다.
- 2 * 64비트 double precision
- 4 * 32비트 double precision
- 명령은 동시에 작동한다.
- SIMD
행렬 곱셈
- 최적화되지 않은 코드
1. void dgemm (int n, double* A, double* B, double* C)
2. {
3. for (int i = 0; i < n; ++i)
4. for (int j = 0; j < n; ++j)
5. {
6. double cij = C[i+j*n]; /* cij = C[i][j] */
7. for(int k = 0; k < n; k++ )
8. cij += A[i+k*n] * B[k+j*n]; /* cij += A[i][k]*B[k][j] */
9. C[i+j*n] = cij; /* C[i][j] = cij */
10. }
11. }
- 최적화 C 코드
1. #include <x86intrin.h>
2. void dgemm (int n, double* A, double* B, double* C)
3. {
4. for (int i = 0; i < n; i+=8)
5. for (int j = 0; j < n; ++j)
6. {
7. __m512d c0 = _mm512_load_pd(C+i+j*n); // c0 = C[i][j]
8. for( int k = 0; k < n; k++ )
9. { // c0 += A[i][k]*B[k][j]
10. __m512d bb = _mm512_broadcastsd_pd(_mm_load_sd(B+j*n+k));
11. c0 = _mm512_fmadd_pd(_mm512_load_pd(A+n*k+i), bb, c0);
12. }
13. _mm512_store_pd(C+i+j*n, c0); // C[i][j] = c0
14. }
15.}
- 최적화 x86 어셈블리 코드
vmovapd (%r11),%zmm1 # Load 8 elements of C into %zmm1
mov %rbx,%rcx # register %rcx = %rbx
xor %eax,%eax # register %eax = 0
vbroadcastsd (%rax,%r8,8),%zmm0 # Make 8 copies of B element in %zmm0
add $0x8,%rax # register %rax = %rax + 8
vfmadd231pd (%rcx),%zmm0,%zmm1 # Parallel mul & add %zmm0, %zmm1
add %r9,%rcx # register %rcx = %rcx
cmp %r10,%rax # compare %r10 to %rax
jne 50 <dgemm+0x50> # jump if not %r10 != %rax
add $0x1, %esi # register % esi = % esi + 1
vmovapd %zmm1, (%r11) # Store %zmm1 into 8 C elements
오른쪽 시프트와 나눗셈
- i 자리만큼 왼쪽으로 시프트하면 정수에 2^i를 곱한다.
- 오른쪽 쉬프트는 2^i로 나눌까?
- 부호 없는 정수에만 해당
- 부호 있는 정수의 경우
- 산술 오른쪽 시프트 : sign 비트(MSB) 복제
- 예: -5 / 4
- 111110112 >> 2 = 111111102 = -2
- -무한대 방향으로 반올림
- 참조 11111011_2 >>> 2 = 001111102 = +62
결합법칙(Associativity)
- 병렬 프로그램은 예상치 못한 순서로 작업을 인터리브할 수 있다.
- 결합법칙에 대한 가정이 실패할 수 있다.
- 정수는 결합법칙이 잘 작동하지만 부동소수점은 결합법칙이 적용되지 않는다.
(x+y)+z | x+(y+z) | ||
x | -1.50E+38 | -1.50E+38 | |
y | 1.50E+38 | 0.00E+00 | |
z | 1.0 | 1.0 | 1.50E+38 |
1.00E+00 | 0.00E+00 |
- 다양한 수준의 병렬 처리에서 병렬 프로그램을 검증해야 한다.
누가 부동소수점 정확도에 관심이 있는가?
- 과학 코드에 중요
- 하지만 일상적인 소비자 사용을 위해서는?
- “내 은행 잔고가 0.0002¢ 부족해요!”
- 하지만 일상적인 소비자 사용을 위해서는?
- 인텔 펜티엄 FDIV 버그
- 시장은 정확성을 기대한다.
- Colwell, The Pentium Chronicles 참조
'컴퓨터시스템구조' 카테고리의 다른 글
[컴퓨터시스템구조] 19. 프로세서(논리 설계, 조합 요소, 논리 요소) (0) | 2023.11.06 |
---|---|
[컴퓨터시스템구조] 17. 부동 소수점 (1) | 2023.10.15 |
[컴퓨터시스템구조] 16. 컴퓨터 산술(mult, mfhi, mflo, mul, div) (0) | 2023.10.15 |
[컴퓨터시스템구조] 15. MIPS 배열 vs. 포인터 (0) | 2023.10.15 |
[컴퓨터시스템구조] 14. MIPS Sort (1) | 2023.10.15 |