[Naver Cloud Camp 7] 교육 정리

네이버 클라우드 캠프 21일차 230523

우기37 2023. 5. 23. 21:14

#1 교육정리

1) 컴파일 vs 인터프리터 방식 복습

2) java에서의 hybrid 방식 복습

3) function prototype

4) JIT & AOT

5) 파일 다운로드 및 검증

 

 

 

 

#2 Compile vs Interpreter

컴파일 방식과 인터프리터 방식은 지난 블로그에서 정리를 해보았지만, 너무나 중요한 내용이기에 오늘 강사님께서도 또 한번 강조를 하신 내용입니다. 관련해서 조금 더 깊게 많은 내용을 정리 해보도록 하겠습니다.

 

Compiler)

  1. 프로그램 Runtime 전에 전체 소스 코드를 검사하여 machine code로 변환합니다.
  2. 소스 코드를 분석하는데 많은 시간이 들지만 전체 실행 시간은 Interpreter로 변환한 코드보다 비교적 빠릅니다.
  3. 링킹-linking(여러개의 object file을 하나의 실행 할 수 있는 프로그램 또는 파일로 만드는 작업)을 추가로 필요로 하는 중간 Object Code(컴파일러 또는 어셈블러가 소스 코드 파일을 이진 코드로 변환하여 생성하는 파일)를 생성하여 더 많은 메모리를 필요로 합니다.
  4. 전체 코드를 검사한 후에 오류 메시지를 생성하여 실행 전에 오류를 발견할 수 있다.
  5. C, C++, Java와 같은 언어들이 Compiler를 사용.
  6. 고급 프로그래밍 언어를 저급 프로그래밍 언어로 바꿔주는 역할을 합니다.

 

 

 

 

Interpreter)

  1. 프로그램 Runtime(실행시간)에 한 번에 한 문장씩 변환합니다.
  2. 소스 코드를 분석하는데 걸리는 시간은 적지만 전체 실행 시간은 한 문장(줄)씩 변환하고 실행하여 Compiler로 변환한 코드보다 상대적으로 더 느립니다.
  3. 중간 Object Code(목적 코드)가 만들어지지 않아 메모리 효율이 높습니다.
  4. 첫 오류를 만날 때까지 프로그램을 계속 번역하고 오류를 만나면 중지되며, 프로그램 실행 전에 오류를 발견하기 어렵습니다.
  5. Python, Ruby와 같은 언어들이 Interpreter를 사용.

 

이미지출처 : https://losskatsu.github.io/os-kernel/compiler-interpreter/#1-%EC%BB%B4%ED%8C%8C%EC%9D%BC%EB%9F%ACcompiler

 

Compiler vs Interpreter)

  • 컴파일러는 전체 프로그램을 담당하며 소스 코드를 분석하는데 많은 시간이 필요합니다. 반면에 인터프리터는 한 줄의 코드를 분석하는데 시간이 거의 걸리지 않습니다.
  • 컴파일된 코드는 더 빠르게 실행되는 반면에 인터프리터 코드는 더 느리게 실행됩니다.
  • 컴파일러는 컴파일 후 모든 오류를 표시합니다. 코드에 실수가 있으면 컴파일되지 않습니다. 반면에 인터프리터는 각 줄의 오류를 하나씩 표시합니다.
  • 인터프리터컴파일을 완전히 대체하지 않습니다.
  • 컴파일러는 더 빠른 성능과 더 작은 메모리 공간과 같은 최적화 이유로 인터프리터를 포함할 수 있습니다.

 

4 types of interpreters)

 

1. Bytecode Interpreter

바이트코드 해석 및 JIT(Just-In-Time) 컴파일 경향으로 인해 컴파일러와 인터프리터 간의 구분이 모호해졌습니다. 바이트코드 인터프리터는 각 명령어가 바이트로 시작하는 최대 256개의 명령어를 처리할 수 있습니다. 

 

2. ThreadedCode Interpreter

바이트코드 인터프리터와 달리 스레드 코드 인터프리터는 바이트 대신 포인터를 사용합니다. 각 명령어는 함수 또는 명령어 시퀀스를 가리키는 단어이며 뒤에 매개변수가 올 수 있습니다. 서로 다른 명령어의 수는 사용 가능한 메모리와 주소 공간에 의해 제한됩니다.

Open Firmware 시스템에서 사용되는 네 번째 코드는 스레드 코드의 전형적인 예입니다. 소스 코드는 가상 머신이 해석하는 "F 코드"라는 바이트코드로 컴파일됩니다.

 

3. Abstract Syntax Tree Interpreter

TypeScript 아키텍처에 어느 정도 익숙한 TypeScript 개발자 라면 추상 구문 트리(AST)에 대해 들어봤을 것입니다.

AST는 소스 코드를 최적화된 추상 구문 트리로 변환한 다음 이 트리 구조에 따라 프로그램을 실행하거나 이를 사용하여 네이티브 코드를 적시에 생성하는 접근 방식입니다.

AST는 전역 프로그램 구조와 명령문 간의 관계를 유지합니다. 이를 통해 시스템은 런타임 동안 더 나은 분석을 수행할 수 있으며 AST는 바이트코드 표현보다 적시 컴파일러를 위한 더 나은 중간 형식이 됩니다.

그러나 통역사의 경우 AST는 더 많은 오버헤드를 발생시킵니다. 추상 구문 트리를 걷는 인터프리터는 바이트코드를 생성하는 인터프리터보다 느립니다.

 

4. Justin-In-Time Compilation

JIT(Just-In-Time Compilation)는 중간 표현이 런타임에 원시 기계 코드로 컴파일되는 기술입니다.

 

 

10 types of compilers)

 

1. Cross-Compiler

크로스 컴파일러는 자신이 생성하는 코드가 실행될 CPU 또는 운영 체제와 다른 컴퓨터에서 실행되는 컴파일러입니다.

 

2. Native Compiler

네이티브 컴파일러는 컴파일러 자체와 동일한 유형의 컴퓨터 및 운영 체제에서 실행되는 출력을 생성합니다.

 

3. Bootstrap Compiler

부트스트랩 컴파일러는 컴파일하려는 언어로 작성된 컴파일러입니다.

 

4. Decompiler

디컴파일러는 낮은 수준의 언어에서 높은 수준의 언어로 코드를 번역합니다.

 

5. Source To Source Compiler(Transpiler)

source to source 컴파일러는 고급 언어 간에 변환하는 프로그램입니다. 이 유형의 컴파일러는 트랜스컴파일러 또는 트랜스파일러라고도 합니다.

트랜스파일러의 몇 가지 예는 다음과 같습니다.

  • Emscripten : C/ C++ 를 JavaScript로 변환합니다.
  • Babel : ES6+에서 ES5로 JavaScript 코드를 변환합니다.
  • Cfront: C++용 원본 컴파일러(1983년경부터). C를 대상 언어로 사용하고 들여쓰기 스타일과 멋진 C 중간 코드가 없는 C 코드를 생성했습니다. 생성된 코드는 일반적으로 사람이 읽을 수 있도록 의도되지 않았기 때문입니다.

 

6. A Language Rewriter

이것은 일반적으로 언어 변경 없이 표현 형식을 번역하는 프로그램입니다.

 

7. Bytecode Compiler

고급 언어를 바이트코드 해석기 또는 가상 머신이 해석할 수 있는 간단한 중간 언어로 변환하는 컴파일러입니다. 예를 들면 다음과 같습니다. Java 및 Python용 바이트코드 컴파일러.

 

8. Just In Time Compiler

JIT 컴파일러는 runtime 까지 컴파일을 연기합니다. 일반적으로 인터프리터 내부에서 실행됩니다.

JIT 컴파일러의 예는 다음과 같습니다.

  • 가장 초기에 출판된 JIT 컴파일러는 1960년에 LISP에 기인합니다.
  • 후자의 기술은 1980년대에 Smalltalk와 같은 언어에서 나타났습니다.
  • 그 이후로 JIT 컴파일은 Java, .NET Framework, Python 및 대부분의 최신 JavaScript 구현과 같은 최신 언어에서 주류로 주목을 받았습니다.

Java 에서 소스 파일은 먼저 컴파일되고 고도로 최적화된 명령 집합인 Java 바이트코드가 포함된 .class 파일로 변환된 다음 바이트코드 인터프리터가 바이트코드를 실행하고 나중에 JIT 컴파일러가 바이트코드를 기계 코드로 변환합니다 .

Java 바이트코드는 런타임 시 가상 머신에 의해 해석되거나 로드 시 또는 런타임 시 네이티브 코드로 컴파일될 수 있습니다. 최신 JVM 구현은 컴파일 방식을 사용하므로 초기 시작 시간 이후에는 성능이 네이티브 코드와 동일합니다.

 

9. AOT Compiler

AOT(Ahead-of-Time) 컴파일은 런타임 전에 상위 수준 프로그래밍 언어 또는 Java 바이트코드와 같은 중간 표현을 컴파일하는 접근 방식입니다.

이에 대한 예는 Angular 프레임워크입니다. 이는 AOT(ahead-of-time) 컴파일러를 사용하여 빌드 시간 동안 HTML 및 TypeScript 코드를 JavaScript 코드로 변환하여 나중에 코드가 실행 중일 때 브라우저에서 더 빠른 렌더링을 제공합니다.

 

10. Assembler

어셈블러는 사람이 읽을 수 있는 어셈블리 언어를 기계 코드로 변환합니다. 이 컴파일 프로세스를 어셈블리라고 합니다. 기계 코드를 어셈블리 언어로 변환하는 역 프로그램을 디스어셈블러라고 합니다.

어셈블리 언어 (ASM)는 기계 코드 명령어에 의존하는 저수준 프로그래밍 언어입니다. 이것이 바로 모든 어셈블리 언어가 정확히 하나의 특정 컴퓨터 아키텍처용으로 설계된 이유입니다.

 

 

 

#3 Java에서의 hybrid

사용자에 의해 작성된 프로그램이 컴파일러에의해 중간코드로 변환되고, 이는 다양한 형태의 서로 다른 시스템에서 인터프리터에 의해 직접 실행

* 중간코드 : 컴퓨터에서 직접 실행될 수 없는 코드로서 컴퓨터 하드웨어에 독립적인 코드

* 이식성 : 원시 프로그램을 다른 기종으로 옮기는 것이 얼마나 용이한가를 나타내는 정도. 다른 기종에 다시 컴파일하기만 해도 바로 돌아가는 프로그램은 이식성이 높은 것이다.

  •  이식성*이 뛰어남
  •  한번 작성된 프로그램은 어떤 컴퓨터 시스템에서든지 즉시 실행 가능
  •  인터프리트 방식의 단점인 소스프로그램의 공개과 컴파일러 방식의 단점인 특정 컴퓨터에 종속적이라는 단점을 해결
  •  최근의 언어에 주로 사용
  •  사용 언어 : JAVA, C#

 

 

 

#4 Function Prototype

컴퓨터 프로그래밍에서 함수 프로토타입 또는 함수 인터페이스는 함수의 이름과 유형 서명 ( arity , 매개변수 데이터 유형  반환 유형 )을 지정하지만 함수 본문을 생략하는 함수  선언 입니다 .

 

함수 정의는 함수가 수행하는 작업("구현")을 지정하는 반면 함수 프로토타입은 단지 인터페이스, 즉 어떤 데이터 유형이 들어오고 나가는지를 지정합니다. "함수 프로토타입"이라는 용어는 특히 프로그래밍 언어의 맥락에서 사용됩니다.C  C++ 에서는 헤더 파일 함수 전방 선언을 배치하여 프로그램 번역 단위 분할  있습니다 .

 

프로토타입에서 매개변수 이름은 선택 사항입니다(그리고 C/C++에는 함수 프로토타입 범위가 있습니다 . 즉 범위가 프로토타입의 끝에서 끝남을 의미합니다). 유형은 모든 수정자와 함께 필요합니다(예: 포인터 또는 const 매개변수 에 대한 참조 ) const 만 제외.

객체 지향 프로그래밍에서 인터페이스 추상 메서드 거의 동일한 목적을 수행합니다 .

 

 

 

 

#5 JIT & AOT

 

JIT - Just In Time Compilation)

AOT와 반대되는 성향을 가지며, 브라우저에서 파일들을 다운로드 한 뒤에 한번 컴파일해서 브라우저 엔진이 실행 할 수 있는 저수준 언어로 바꾼 후 화면을 렌더하는 방식입니다.

 

소스코드가 server에서 컴파일되는 것이 아니라 사용자의 브라우저에서 컴파일되는 방식이기 때문에 AOT 방식에 비해서 컴파일 하는데 어느 정도의 시간이 소요됩니다. 마찬가지의 이유로 컴파일 에러도 사용자의 브라우저에서 나타나기 때문에 사용자의 브라우저에서 갑자기 App이 멈춘다거나 에러가 생기는 일이 발생 할 수 있습니다.

 

컴파일러를 사용자의 브라우저로 전송해야 컴파일이 가능하기 때문에 AOT에 비해 번들 사이즈가 크다는 특징도 있습니다. 하지만 브라우저에서 여러 에러들을 발생시키고, 확인 할 수 있다는점 때문에 local에서 테스트를 진행 할 때 JIT 컴파일 방식을 사용하기도 합니다. 브라우저에서 디버깅이 가능하다는 것은 로컬 개발시에 장점입니다.

 

Java 프로그램을 컴파일 할 때 javac 명령줄 도구를 사용하여 플랫폼(CPU 아키텍쳐 + os 예로, 인텍맥은 인텍 cpu + MacOS를 사용하는 플랫폼)에 독립적인 중간 표현(IR, JVM 바이트 코드가 IR의 일종)으로 변환되며, 이 JVM 바이트 코드는 JVM이 읽기에는 아주 쉽지만, 반대로 사람이 있기는 어렵습니다. 우리 컴퓨터의 기존 프로세서는 JVM 바이트 코드를 직접 실행 할 수 없으며, 그렇게 하기 위해 컴파일러는 다시 JVM 바이트 코드를 플랫폼에 종속적인 기계어로 변환해야만 하며, 프로그램이 본래 컴파일되었던 플랫폼과 동일한 플랫폼에서만 실행 할 수 있음을 의미합니다. 즉, 인텍맥에서 컴파일된 코드는 인텔 맥에서만 실행 할 수 있음을 의미합니다.

 

그리고 이것들은 정확히 바이트 코드 컴파일러가 하는 작업입니다. 여기서 컴파일러는 바이트 코드를 기계어로 변환하는 JVM 내부의 인터프리터를 의미합니다. 즉, JAVA는 기본적으로 정적 컴파일 방식과 동적 컴파일 방식을 함께 사용합니다.

 

JVM은 런타임에 현재 어떤 플랫폼에서 프로세스가 실행되고 있는지를 파악하고 바이트 코드를 해석한 후 현재 플랫폼에 최적화된 기계어를 만들어 냅니다. 이 전략은 동적 컴파일의 한 형태인 JIT 컴파일로 알려져 있으며, JVM의 기본 JIT 컴파일러는 일명 Hotspot으로 알려져 있습니다.

 

 

 

AOT - Ahead Of Time Compilation)

소스 코드를 미리 컴파일하는 방식을 의미합니다. build를 통해 번들링 작업을 실행하지만, 브라우저가 번들링 된 코드를 바로 싱핼 할 수 있는 것은 아닙니다. 브라우저가 어떤 엔진을 사용하는지에 따라 브라우저는 각각의 js 파일들을 머신 코드등의 저수준 코드러 다시 컴파일 한 후에 실행합니다.

 

AOT방식은 이를 빌드 타임에 컴파일 해주기 때문에 브라우저가 Angular App을 저수준 코드로 다시 컴파일 할 필요 없이 바로 실행 할 수 있습니다. 기존의 방식에서 컴파일에 소요되는 시간이 빠지기 때문에 더 빠른 랜더가 가능하다는 장점이 있습니다. 또한 서버에서 이미 컴파일된 파일을 제공하므로 클라이언트에서 http 요청을 보냈을 때 컴파일러를 보낼 필요가 없습니다.

 

다시말해서 vendor.bundle.js 파일에서 컴파일러를 제외하므로 번들 사이즈가 많이 작아지게 됩니다. 참고로 Abgular 컴파일러는 일반적으로 용량이 큽니다.

 

템플릿 바인딩 에러등을 빌드 타임에 미리 잡을 수 있다는 점이 있기 때문에 AOT 방식은 실제 프로덕션 과정에서 자주 사용되는 방식입니다.

 

AOT 컴파일은 프로그램이 실행되기 전에 소스 코드를 기계어로 변환하는 방식으로 정적 컴파일의 한 형태입니다. 이것은 C와 같은 오래된 프로그래밍 언어의 코드가 정적으로 링크되고 컴파일되는 구형 방식입니다. 컴파일의 결과로 얻어진 기계어는 특정 플랫폼에 맞게 조정되며 매우 빠른 실행이 가능해집니다.

 

 

 

#6 실행 파일 다운로드 및 검증