Compile
프로그램 개발
Program은 여러 source file들로 구성되어 있다.
- Develop : 각 모듈로 나눠서 개발을 진행한 후
- Compile : 각 모듈은 compile을 object file을 생성한다.
- Linking : 만들어진 object file들을 합쳐서 하나의 executable file을 생성한다.
Compile 과정과 linking 과정을 합쳐서 compile이라고도 한다.
Compile
Compiler는 program(source code)을 machine instruction으로 변환한다.
만들어진 object file에는 다음과 같은 정보가 담겨있다.
- Header
- object module의 내용을 담고 있다.
- 이름, text size, data size 등
- Text segment
- Instruction들이 machine instruction으로 translate 되어 있다.
- Static data segment
- Program life 동안 필요한 static data들이 존재한다.
- Relocation information
- Load된 프로그램의 absolute location에 의존하는 내용을 담고 있다.
- 예를 들어, external symbol들이 어디서 사용되는지를 나타낸다.
- Symbol table
- Global definition과 external references들을 모아놓은 것이다.
- External symbol들을 모아놓은 것이다.
- Debug information
- 디버깅 할 때 디버거를 위한 추가적인 정보를 담는다.
External Symbols
External variables + function's name 을 말한다.
External symbols의 주소는 compile time에서는 확정되지 않고, linking이 종료된 후 결정된다.
따라서 object file이 만들어지더라도, 채울 수 없는 부분이 있다.
Compiler는 이를 0으로 표시하고, linker가 제대로 채울 수 있도록 정보를 남겨놓는다.
- Symbol table을 이용해서 external symbols들을 모아놓는다.
- 이 symbol들이 어디서 사용되는지를 relocation information table에 모아놓는다.
Linking
Executable image를 만드는 과정이다.
Linking을 통해 external variable과 function들(external symbol)의 최종 주소가 결정된다.
- Merge segments
- 여러 object file들을 하나로 합친다.
- Resolve labels
- determine thier addresses
- Patch location-dependent and external references
- 미완성인 부분들을 채워준다.
Static Linking v.s. Dynamic Linking
두 개의 linking에 사용되는 library의 형태가 다르다.
- Static linking
- *.o 파일 사용
- 명령어 : <pre>gcc hello.c -o hellocstatic -static</pre>
- 간단한 파일도 linking을 거치면서 전체 library가 들어가서 최종 image의 크기가 커진다.
- Library의 코드를 변경하면 compile을 다시 해야 한다.
- Dynamic linking
- = lazy linkage, delayed linkage
- *.so 파일 사용 (shared object)
- 명령어 : <pre>gcc hello.c -o hello</pre>
- .c와 .so을 compile time에 결합하지 않고, Linking이 일어날 수 있는 정도의 정보만 만든다.
- 실제 library가 들어가지 않아서 프로그램의 크기가 많이 커지지 않는다.
- 실제 linking은 프로그램 실행 또는 library의 함수를 호출(actual call) 할 때 발생한다.
- 호출될 때 library procedure을 link/load 한다.
- Dynamic linking은 프로그램 실행 중 library가 들어오기 때문에 stack과 heap 사이 어딘가에 위치한다.
- 따라서 procedure code는 relocatable(position-independent) 해야 한다.
- 어느 위치에 있어도 작동할 수 있어야 한다.
- Library을 변경하더라도 자동으로 최신의 library 내용을 가져다 사용한다.
Dynamic Linking 의 장점
불필요하게 image 크기가 커지는 것(image bloat)을 피할 수 있다.
또한, 자동으로 새로운 library version을 pick up한다.
따라서 software management에 유리하다.
Loading
만들어진 disk의 executable image file을 memory에 load한다.
- header을 읽어서 segment의 크기를 결정한다.
- virtual address space을 생성한다.
- virtual address space는 process마다 존재한다.
- memory에 text와 initialized data을 copy한다.
- 또는 page table entry를 set 한다.
- 모든 data와 code를 copy하는 것이 아니라, 공간만 만들고 필요한 내용은 나중에 채워진다.
- stack에 main이 받는 argument를 set up 한다.
- register를 초기화한다.
- <pre>$sp, $fp, $gp</pre> 등
- startup routine으로 jump한다.
- main이 받는 argument copy하고 main 함수를 호출한다.
- main이 return되면 "exit" system call을 수행한다.
예시
Program Execution
Compiler approach vs. Interpreter approach
- Compiler approach
- = native mode
- HLL을 compile하여 machine code(ISA)로 변환한다.
- 실제 실행되는 것은 compile 된 ISA 이다.
- 속도가 빠르기 때문에 kernel, embedded system, OS 등을 설계하는데 많이 사용된다.
- 예 : C 언어
- Interpreter approach
- = non-native mode
- HLL을 ISA로 compile하지 않는다.
- 프로그램 실행은 interpreter가 동작하는 것이다.
- Interpreter는 코드를 한 줄 씩 machine instruction으로 바꿔서 실행해준다.
- compile approach에 비해 속도가 느리다.
- 코드를 수정하면 결과를 바로 볼 수 있기 때문에 interactive task에 주로 사용된다.
- 예 : Python
Machine learning을 Python으로 하는 이유?
Machine learning은 많고 빠른 계산을 요구한다.
그런데 python은 non-native mode로 동작하기 때문에 실행속도가 느리다.
그러나 machine learning에 사용되는 모듈들은 이미 native mode로 compile되어 있는 것이기 때문에 빠르게 계산 수행이 가능한 것이다.
JAVA
HLL로 작성된 source code를 ISA까지가 아닌 Jaba bytecode(*.class)로 compile을 한다.
이를 구동하기 위해 JVM(Java Virtual Machine)이 필요하다.
따라서 non-native mode로 볼 수 있다.
다만, JVM은 full interpreter approach에 비해 훨씬 작기 때문에 만드는데 큰 어려움이 없다.
Profiling
Native mode의 속도에 따라가기 위하여 JVM은 프로그램을 실행하면서 코드의 어느 부분이 자주 사용되는 가를 체크한다.
자주 사용되는 것이 있으면 실행 중 native mode로 compile을 수행하여 호출한다.
이를 통해 전체적인 속도를 향상시킬 수 있다.
Linking in Java
Compile 된 java bytecode 들을 사전에 하나의 executable file로 합치지 않는다.
JVM이 실행하면서 linking을 수행한다.
Language와 Algorithm의 영향
개발하려는 프로그램에 대해 아래처럼 비교를 수행해서 어떤 language, algorithm이 적합한지 확인해야 한다.
프로그램 고유의 특성으로 볼 수 있다.
Bubblesort Relative Performance 그래프
Java/int는 non-native mode이기 때문에 C에 비해 성능이 월등히 낮다.
그러나 Java/JIT는 도중에 코드를 compile 하였기 때문에 성능이 높다.
Compiler
컴파일러가 하는 일은 크게 2가지로 구분할 수 있다.
- Parsing
- Language dependent
- 선택된 programming language의 syntax에 맞는지 확인한다.
- Programming language에 정의된 syntactic에 따라 intermediate code로 변환된다.
- Optimization
- Machine dependent
- 각 machine(CPU)에 맞는 machine instruction으로 변환한다.
- 어떻게 더 빠르게 할 수 있는가를 위해 optimization에 중점을 둔다.
Parsing Algorithms
Automata theory and formal language(grammars)
주로 1970년대까지 활발하게 연구되었다.
Compiler and Computer Architecture
RISC가 발전하기 이전의 compiler 연구는 parsing에 초점을 두었다.
Compiler와 computer architecture에 대한 연구는 별개로 취급되었다.
이후 RISC가 발전하면서 IC(instruction count)을 줄이기 위하여 smart compiler에 대해 연구가 시작되었다.
이 때 부터 compiler와 computer architecture에 대한 연구가 연관되어 진행되었다.
GPU and NPU
GPU와 NPU을 위해 compiler는 어떻게 해야 하는가에 대해서도 연구가 진행되고 있다.
Optimization Level
gcc의 optimization level로 3가지가 제공된다.
높은 level의 optimization을 사용할 수록 compile의 안전성이 낮아진다.
따라서 어떤 optimization level을 선택할 것인가를 잘 결정해야 한다.
- -o0 : optimization 안함
- -o1 : branch block에서 optimization 진행
- -o2 : across branch block에서 optimization 진행
- -o3 : 굉장히 공격적인 optimization
Compiler Optimization의 영향
같은 CPU에서 돌리기 때문에 cct는 고정된다.
Optimization을 진행함에 따라 IC가 크게 변동되고, 이로 인해 clock cycle이 변화한다.
Performance는 IC*CPI*cct에 대해 반비례 하기 때문에 다음과 같은 성능 그래프가 나타난다.
IC을 줄이기 위해 smart compiler을 사용한다.
'Computer Science > Computer Architecture' 카테고리의 다른 글
3. Integer Arithmetic (0) | 2021.05.16 |
---|---|
3. Computer Arithmetic (0) | 2021.05.16 |
2. MIPS : Runtime Environment (0) | 2021.05.09 |
2. MIPS : Non-Leaf Procedure (1) | 2021.05.09 |
2. MIPS : Program Execution (0) | 2021.04.22 |
댓글