컴퓨터구조

[컴퓨터구조] RISC-V 피보나치 수열 어셈블리

Below_zero 2025. 3. 9. 04:05

 

컴퓨터구조 수업 프로젝트로 어셈블리 코딩을 진행했는데, 단순재귀는 할만했지만 메모이제이션을 사용하는게 꽤 힘들었던 기억이 난다. 너무 로우레벨이라 생각하는 방식을 평소 코딩이랑 다르게 해야해서 까다로웠다.

 

먼저 단순 재귀로만 구현한 피보나치

# execute시 t5에 input, t6에 output 출력됨

li x31, 0             # x31 = output = answer
addi x30, x0, 20      # x30 = input (유지) , 20 넣을시 0x1a6d(6765) 출력
add x5, x0, x30       # N (계산을 위해 사용)

jal fib                 # Call the fib function

j END                   # End of program

fib:
    # Allocate stack space
    addi sp, sp, -16    
    sw ra, 12(sp)       # return address 저장
    sw s0, 8(sp)        # s0 (temporary storage) 저장
    sw x5, 4(sp)        # input x5 저장


#if n == 0 return 0,
#if n == 1 return 1 처리     
fib_zero_or_one:
    addi x31, zero, 0   
    beq x5, x0, finish   # If x5 == 0, return 0
    addi x31, x31, 1    
    beq x5, x31, finish   # If x5 == 1, return 1

fib_n_1:
    addi x5, x5, -1     # x5 = x5 - 1
    sw x5, 0(sp)        # Save x5 to stack
    jal fib             # Call fib(n-1)
    lw x5, 0(sp)        # Restore x5 from stack
    addi x5, x5, -1     # x5 = x5 - 2

fib_n_2:
    add s0, x31, x0     # fib(n-1)의 return 값을 s0에 저장
    jal fib             # fib(n-2) 호출
    add x31, x31, s0    # x31 = fib(n-1) + fib(n-2)

finish:
    lw x5, 4(sp)        # x5 복원
    lw s0, 8(sp)        # s0 복원
    lw ra, 12(sp)       # return address 복원
    addi sp, sp, 16     # 스택할당해제
    jr ra               # Return

END:

RARS를 이용해 시뮬레이션 한 결과, t5에 input, t6에 output이 잘 출력되어 있음

 

단순 재귀로만 구현할 경우 input의 크기가 작을 때 (한 자리수 정도)는 별 체감이 없었으나 input의 크기를 늘릴수록 기하급수적으로 실행시간이 늘어나는 것을 체감할 수 있었다. 최적화의 필요성을 실감했다.

올린 예제처럼 input을 10진수로 20으로 설정후 시뮬레이션하니 밥 한끼 먹고오니 그제서야 완료되있었음.

 

 

다음으론 메모이제이션을 활용한 피보나치 어셈블리

 .data
array: .zero 128           # int table[32], 배열 원소 개수 * 4로 설정, 배열 원소 개수는 n과 동일해야할 듯
.text

# execute시 t5에 input, t6에 output 출력됨

li x31, 0             # x31 = output = answer 
addi x30, x0, 20      # x30 = input (유지) 20 넣을시 0x1a6d(6765) 출력
addi x6, x0, 20      # x6 = size
add x5, x0, x30       # N (계산을 위해 사용)
 
jal fib                 # Call the fib function

j END                   # End of program

fib:
    # Allocate stack space
    addi sp, sp, -16    
    sw ra, 12(sp)       # return address 저장
    sw s0, 8(sp)        # s0 (temporary storage) 저장
    sw x5, 4(sp)        # input x5 저장
    
    blt x5, x6 fib_check # n<size면 table[n]!=0을 확인하기 위해 이동
    
    
#  table[n] != 0인지 확인   
fib_check:              
    la t1, array        
    slli t2, x5, 2        
    add t1, t1, t2       
    lw t3, 0(t1)         # t3 = table[n]
    bnez t3, fib_return_table # n < size && table[n] != 0 만족시 fib_return_table로
      

#if n == 0 return 0,
#if n == 1 return 1 처리     
fib_zero_or_one:
    addi x31, zero, 0   
    beq x5, zero, finish   # If x5 == 0, return 0
    addi x31, x31, 1    
    beq x5, x31, finish   # If x5 == 1, return 1


fib_n_1:
    addi x5, x5, -1     # x5 = x5 - 1
    sw x5, 0(sp)        # Save x5 to stack
    jal fib             # Call fib(n-1)
    lw x5, 0(sp)        # Restore x5 from stack
    addi x5, x5, -1     # x5 = x5 - 2

fib_n_2:
    add s0, x31, x0     # fib(n-1) return 값 저장 
    
    jal fib             # 함수 fib(n-2) 호출
    
    add x31, x31, s0    # answer = fib(n-1) + fib(n-2)

finish:
    lw x5, 4(sp)        # x5 복원
    lw s0, 8(sp)        # s0 복원
    lw ra, 12(sp)       # return address 복원
    addi sp, sp, 16     # 스택할당해제
    jr ra               # Return

# return table[n]
fib_return_table:
    add x31 x31 t3
    jr ra

END:

 

메모이제이션으로 구현할 경우 단순재귀의 경우보다 실행시간이 월등히 짧아졌음을 확인할 수 있었다.

 

역시 앞 예제와 같은 결과, 하지만 실행시간은 배로 빨랐다