행렬: 두 개의 인덱스, 하나의 주소
행렬은 숫자를 행과 열로 배열한 격자입니다. 행은 격자의 가로 한 줄이며 왼쪽에서 오른쪽으로 읽습니다. 열은 격자의 세로 한 줄이며 위에서 아래로 읽습니다. 메모리에는 행이나 열이라는 개념이 없습니다. 메모리는 하나의 긴 바이트 나열이며, 이전 레슨에서 다룬 벡터와 같은 종류입니다. 행렬을 저장하려면 그 행과 열을 그 하나의 나열 안에 차례대로 놓아야 합니다. 일반적으로 선택되는 것은 행 우선 순서입니다. 즉, 첫 번째 행의 모든 숫자를 저장하고, 그다음 두 번째 행의 모든 숫자를 저장하며, 모든 행을 다 놓을 때까지 계속합니다.
두 개의 인덱스, 하나의 주소
행렬에서 원소의 위치는 두 개의 인덱스로 주어집니다. 행 인덱스는 그 원소가 어느 행에 속하는지를 나타내고, 열 인덱스는 어느 열에 속하는지를 나타냅니다. 둘 다 0부터 세기 시작하며, 벡터 레슨에서 인덱스가 작동한 방식과 같습니다. 메모리는 숫자의 단일한 평탄한 나열을 담으므로, 두 인덱스는 먼저 하나의 평탄화된 인덱스로 결합되어야 합니다. 행 인덱스에 행렬의 열 수를 곱한 다음, 열 인덱스를 더합니다. 거기서 평탄화된 인덱스는 벡터의 인덱스와 같은 방식으로 바이트 주소가 됩니다. address = base + index * 4.
mul t4, t1, t3 # t4 = 행 인덱스 * 열 개수add t4, t4, t2 # t4 = that + 열 인덱스(평탄화된 인덱스)slli t4, t4, 2 # t4 = 바이트 오프셋 = 인덱스 * 4add t5, t0, t4 # t5 = 해당 행과 열에 있는 요소의 주소lw t5, 0(t5) # t5 = 그 주소의 값
는 행 인덱스에 열 수를 곱합니다. 벡터 레슨에서는 4가 2의 거듭제곱이므로 이 곱셈을 시프트로 할 수 있었지만, 행렬의 열 수는 보통 그렇지 않으므로 여기서는 대신 이 사용됩니다. 열 인덱스가 더해지고, 그 결과는 또 다른 로 4만큼 스케일됩니다(4는 2의 거듭제곱이므로 이 부분에는 여전히 시프트가 통합니다). 그러면 주어진 행과 열에 있는 원소의 바이트 오프셋이 나옵니다.
행 0, 열 0은 인덱스 0에 대응합니다.
.data 레이아웃과 여러분 자신의 읽기가 위에 나온 , , 산술을 사용해 그 단일 행에 부여하는 형태입니다. 격자와 평탄화된 행이 두 번 표시된 하나의 것으로 이해되면, 같은 패턴이 모든 데이터 격자를 포괄합니다. 이미지의 픽셀 행, 게임판, 또는 스프레드시트입니다. 각각은 하나의 배열이며, 한 번의 추가 곱셈으로 도달합니다.행렬의 합 구하기
.datam: .word 1, 2, 3, 4, 5, 6, 7, 8, 9 # 3x3 행렬, 행 단위로 저장됨.textla t0, m # 기준 주소li t1, 3 # 행 개수li t2, 3 # 열 개수li t3, 0 # 현재 행 인덱스, 0에서 시작li s0, 0 # 누적 합계row_loop:beq t3, t1, row_doneli t4, 0 # 현재 열 인덱스, 0에서 시작col_loop:beq t4, t2, col_donemul t5, t3, t2 # 행 인덱스 * 열 개수add t5, t5, t4 # + 열 인덱스 = 평탄화된 인덱스slli t5, t5, 2 # 바이트 오프셋 = 인덱스 * 4add t6, t0, t5 # 해당 행과 열에 있는 요소의 주소lw t6, 0(t6) # 그 주소의 값add s0, s0, t6 # 합계 += 요소addi t4, t4, 1 # 다음 열로 이동j col_loopcol_done:addi t3, t3, 1 # 다음 행으로 이동j row_looprow_done:mv a0, s0 # 합계 출력: 1+2+...+9 = 45li a7, 1ecallli a7, 10ecall
두 개의 중첩 루프가 행렬을 인덱싱합니다. 바깥 루프는 행을 선택하고 안쪽 루프는 그 행 안의 열을 선택합니다. 는 행 인덱스에 열 수를 곱합니다. 열 인덱스를 더하고 4로 스케일하면 그 원소의 주소가 나옵니다. 그 주소에 저장된 값이 로드되어 실행 중인 합계에 더해집니다. 안쪽 루프가 한 행을 끝내면 바깥 루프는 다음 행으로 이동하며, 3개의 행이 모두 처리될 때까지 계속합니다.
