코드 실행 과정을 컴퓨터 구조 관점에서 봄으로써
프로그래밍 할 때 해당 과정을 항상 생각하며 효율적으로 코드를 구성하는데 도움이 될 수 있습니다.
- 명령어 표현
- 메모리 구조
- C언어 코드의 어셈블링
- 명령어와 프로그램 성능의 관계
- Java에서의 코드 실행 과정
Intel, ARM, MIPS 등 CPU 제조사별로 명령어 포맷이 각각 다릅니다.
명령어와 피연산자로 이루어짐을 알 수 있습니다.
이를 기계어(0,1)로 바꾸어서 CPU가 이를 이해합니다.
MIPS 명령어
add $t0, $s1, $s2
해당 명령어는 이렇게 포맷에 맞추어 기계어로 변경되어 code 영역에 저장됩니다.
000000 10001 10010 01000 00000 100000
총 32bits -> 1 word
명령어를 표현하는 방법에는 다양한 방법이 존재합니다.
즉시 주소지정
op | rs | rt | immediate
레지스터 주소지정
op | rs | rt | rd | shamt | funct
add
, sub
레지스터에 직접 값을 넣는 것
베이스 주소지정
op | rs | rt | constant or offset <-> register
lw
sw
lbu
... 메모리에서 데이터(주소 혹은 데이터 값 자체)를 가지고 오는 것
PC 상대적 주소지정
op | rs | rt | address <-> PC
beq
bne
... PC 레지스터에 주소를 +-해서 명령어 주소 자체를 설정하는 것
다음 명령어로 분기 혹은 점프할 때 사용
의사직접 주소지정
(Pseudodirect addressing)
op | address <-> PC(상위 4bits만)
j
jal
...명령어 주소 자체를 설정
26비트 자체를 가지어 PC 상대적 주소지정보다 더 많은 주소를 가질 수 있다.
Procedure
할 때 많이 사용됩니다.
stack
: 지역변수 할당, 프로시저에서의 프레임
heap
: 동적 데이터 영역, 프로그래머가 직접 공간을 할당
data
: 정적 변수 및 전역 변수 영역
code
: 기계어로 저장된 프로그램 실행 코드 저장 영역, read-only
int fact (int n) {
if (n < 1) return f;
else return n * fact(n - 1);
}
// procedure 시작
fact:
// 직전 frame의 PC(명령어 코드)주소, 레지스터 데이터 저장
addi $sp, $sp, -8
sw $ra, 4($sp)
sw $a0, 0($sp)
// 분기
slti $t0, $a0, 1
beq $to, $zero, L1
addi $v0, $zero, 1
// 스택 포인터(sp) 직전 frame으로 변경(직전 frame이 실행중이였던 명령어 주소가 저장되있음) == frame이 pop된 것
addi $sp, $sp, 8
jr $ra
L1:
addi $a0, $a0, -1
jal fact
lw $a0, 0($sp)
lw $ra, 4($sp)
addi $sp, $sp, 8
mul $v0, $a0, $v0
jr $ra
해당 코드는 procedure(프로시저)를 n번 호출하는 재귀함수 코드입니다.
명령어 코드를 계속 옮기면서 새로운 메모리에 저장
frame
프로시저를 호출하면서 return 주소와 인자를 스택에 저장하고 있습니다.
이로인해 stack 영역에서 새로운 frame이 생성되어 push되는 효과를 가져옵니다.
레지스터에 주소 및 호출 이전 데이터를 저장하고 stack pointer를 통해 주소를 옮기는데, 이것이 stack 자료구조의 push와 pop되는 것과 같습니다.
레지스터의 활용
데이터 끼리 연산을 시키기 위해서는 레지스터를 적극 활용해야 합니다.
레지스터 종류에 따라 지역변수(t0), 전역변수(s0), 정적 변수(상수)가 나누어집니다. 또한 프로시저를 호출하기 위한 인수(a0) 레지스터도 있습니다.
레지스터들과 메모리 사이를 왔다갔다하며 데이터 관리를 수행합니다.
매커니즘
레지스터와 명령어들 하나하나를 다 외우고 이해할 필요는 없습니다.
다만, 이런식으로 명령어 코드를 가져와서 변수 영역에 저장하는 대략적인 흐름을 알고 있다면, 코딩을 할 때 좀 더 효율적으로 변수를 선언하고 사용할수 있습니다.
각 제조사마다 각기 다른 양식의 명령어 구조를 가지고 있기 때문에
상위 수준 언어에서 명령어를 줄여도 컴파일을 통해 어셈블리어를 보면 더 많을수도 있습니다.
또한 어셈블리어의 각 명령어마다 얼마만큼의 클럭을 사용해야 되는지도 따져봐야 하며 (CPI
)
또한 제조사마다 클럭 사이클
의 속도 자체도 다를 수 있습니다.
그래서 무조껀 명령어를 줄인다고 해서 성능이 좋아지는 것은 아니며, 명령어 자체의 성능 및 하드웨어의 성능까지 고려해야 합니다.
자바도 프로그램이기 때문에 위와 같은 구조로 실행됩니다.
다만 JVM의 메모리 구조 및 Class Loader, Interpreter 등에 따라 추가적인 요인이 생겨 좀 더 복잡하게 실행됩니다.
자세한 동작방식은 JVM 동작방식 참조.
명령어 코드
를 저장하는 메모리 공간과, 데이터
를 저장하는 메모리 공간은 따로 구현되어 있습니다. 이들이 서로 유기적으로 동작하여 명령에 맞게 수행하며 데이터를 저장합니다.컴퓨터 구조 및 설계 - 데이비드 A 패터슨 존 헤네시