autocast() 있음autocast는 FP32 → FP16 → FP32 간 dtype 변환 오버헤드가 있으며, 이 변환 과정에서 오히려 느려질 수 있음
scripted_model = torch.jit.trace(model, example_input)jit.trace의 의미
TorchScript는 Python이 없어도 모델을 실행할 수 있게 만든 “고정된 계산 그래프(frozen graph)” 형태

conv1.weight: 필터
합성곱 → repeat
미분은 sum!
pool_size=(2,2), sride=2(N*OH*OW, C*PH*PW) → col(4,4)핵심은 forward된 자리에만 미분한다는 점
(모든 선택적 연산은 미분이 동일)
핼렬곱의 backward & repeat의 backward
def forward(self, x):
x = self.conv(x)
x = self.bn(x)
x = self.relu(x)
return x
trainable_params = sum(p.numel() for p in self.model.parameters() if p.requires_grad)point: 스케일 팩터, 영점(zero point)
“딥러닝의 양자화(quantization)”와 “물리학에서의 양자화(quantization)”는 공통된 철학적 뿌리를 공유하지만, 의미와 맥락은 완전히 다름
→ “연속적인 것을 이산 단위로 표현한다”는 공통된 사고에서 출발하지만, 적용되는 대상이 다릅니다.
| 구분 | 공통된 핵심 아이디어 |
|---|---|
| 용어 의미 | 연속적인 물리량 또는 수치를 한정된 단위(quantum)로 나눈다 |
| 이산화(Discretization) | 무한한 상태를 유한한 단계로 근사 표현함 |
| 적용 의도 | 계산 효율성 향상 또는 물리적 현실 반영 |
물리학에서 “양자화(quantization)”는 고전역학적 연속량(에너지, 운동량 등)이 불연속적인 에너지 준위(quantum)로만 존재한다는 이론적 전환을 의미합니다.
예:
핵심:
자연계의 연속적 현상을 근본적으로 이산적 단위(quantum) 로 설명하려는 물리학적 패러다임입니다.[5]
딥러닝의 양자화는 모델의 가중치(weights)와 활성값(activations) 을
float32(32비트 부동소수점) 같은 연속적 실수 표현 대신,
int8, int4 등 이산적 정수 표현으로 근사하는 최적화 기법입니다.
즉, 연속적으로 표현된 실수값들을 작은 정수 집합으로 매핑하여:
예:
실제 가중치: [-0.87, 0.11, 0.92]
양자화된 값: [-4, 0, 4] (INT4 기준)
스케일링 팩터: scale = 0.23
이렇게 모델을 압축하지만, 신호의 의미는 유지됩니다.
| 구분 | 물리 양자화 | 딥러닝 양자화 |
|---|---|---|
| 본질 | 자연 현상의 이산화 | 수치 근사(연속 → 정수) |
| 대상 | 에너지, 운동량 등 물리량 | 가중치·활성값 (실수 데이터) |
| 목적 | 자연 규칙 설명 | 계산 최적화, 속도·메모리 절약 |
| 수학적 성격 | 에너지 고유값 문제 해결 (양자역학 방정식) | 부호화/정규화, 스케일링 기반 근사 |
| 불연속 단위 | 플랑크 상수(h) | 정수 정밀도(INT8 등) |
| 수행 방식 | 물리 법칙의 본질적 성질 | 하드웨어 친화적 근사(압축, 효율화) |
관념적으로:
딥러닝의 양자화는 물리적 양자화 개념에서 “연속적인 것을 불연속적으로 표현한다(quantum)”는 철학을 차용한 비유적 확장 개념입니다.
실질적으로:
물리학의 양자화는 자연의 근본 속성 설명,
딥러닝의 양자화는 계산 자원의 최적화를 위한 기술적 근사입니다.
즉, 두 개념은 이름과 철학적 기원은 같지만,
하나는 자연의 본질을 기술하고, 다른 하나는 계산의 효율을 높이는 수학적 기법입니다.
| 유형 | 스케일 s | 제로포인트 z | 특징 |
|---|---|---|---|
| Affine(비대칭) | 0.00784 | 0 | 일반적인 INT8 양자화 |
| Symmetric(대칭) | 0.00784 | -1(또는 0) | 0을 정확히 중앙으로 맞추는 경우 |
부동소수점 co-processor 있음
임베디드 장치에 심으려면 양자화 필수
양자화 인식 학습
미니배치 782
classification에서 이진분류/다중분류 중 다중분류가 더 어려운 거랑 헷갈려요
→ “loss 계산이 어렵다” 의미와 “문제가 어렵다” 의미가 다른 개념이라서 많이 혼동되는 부분 (계산 복잡도 vs. 학습 난이도)
threshold = 0.01
if np.abs(w) < threshold:
yieldp.607
LSTM → "기억 셀"(p.731)
(p.775)
Chapter 1 언어 소개(A Tutorial Introduction)
Chapter 2 형, 연산자, 수식(Types, Operators, and Expressions)
Chapter 3 제어 흐름(Control Flow)
Chapter 4 함수와 프로그램 구조(Functions and Programm Structure)
Chapter 5 포인터와 배열(Pointers and Arrays)
Chapter 6 구조체(Structures)
Chapter 7 입력과 출력(Input and Output)
Chapter 8 UNIX 시스템과의 인터페이스(The UNIX System Interface)
Appendix A 참조 매뉴얼
Appendix B 표준 라이브러리
Appendix C 개선점 요약
찾아보기
레지스터(register)는 컴퓨터 중앙처리장치(CPU) 내부에 존재하는 고속의 임시 저장 공간
레지스터는 CPU 내에 있으며 프로세서가 작업을 효율적으로 처리할 수 있도록 데이터, 주소, 명령 등 다양한 정보를 임시로 보관하는 매우 빠른 기억 장치임
- 역할과 특징
- CPU가 연산을 하거나 데이터를 처리할 때 필요로 하는 값을 매우 빠른 속도로 저장하고 불러오는 데 사용
- 메모리(RAM)보다 훨씬 적은 용량이지만, CPU와 직접 연결되어 있어서 데이터 접근 속도가 가장 빠름
- 산술 연산, 데이터 이동, 주소 지정, 명령 실행 흐름 제어 등 다양한 용도로 사용 → 프로그램 실행 효율을 크게 높임
- 종류 예시
- 범용 레지스터: 데이터 연산·저장 등 다목적으로 사용
- 프로그램 카운터(PC): 다음에 실행할 명령의 메모리 주소 저장
- 명령어 레지스터(IR): 현재 실행 중인 명령어 보관
- 상태/플래그 레지스터: 연산 결과 상태(오버플로우 등) 저장
CPU 플래그 레지스터는 연산 결과와 CPU 상태를 표현하는 핵심 제어 수단으로, 하드웨어적으로 자동 설정되어 프로그램의 흐름 제어 및 디버깅에 활용된다.
a=3, b=3
a-b
if a == 3:
| CF | OF | SF | ZF |
|---|---|---|---|
| 1 |
ZF==1이면 a와 b는 같다.
unsigned char a=5;
unsigned char b=2;
if a < b: // cmp a,b → a-b → 5-2
…
| 2 | ||||||||
|---|---|---|---|---|---|---|---|---|
| 0 | 0 | 0 | 0 | 0 | 0 | 1 | ||
| - | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 |
| 0 | 0 | 0 | 0 | 0 | 0 | 1 | 1 |
▽
| CF | OF | SF | ZF |
|---|---|---|---|
| 0 | 0 |
CF==0이면 a가 b보다 크다!
unsigned char a=2;
unsigned char b=5;
if a < b: // cmp a,b → a-b → 2-5
…
| 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | |
| - | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 1 |
▽
| 2 | 2 | |||||||
|---|---|---|---|---|---|---|---|---|
| 1 | 1 | 1 | 1 | 1 | 0 | 0 | ||
| - | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 1 |
| 1 | 1 | 1 | 1 | 1 | 1 | 0 | 1 |
▽
| CF | OF | SF | ZF |
|---|---|---|---|
| 1 | 0 |
carry flag가 1이면 a가 b보다 작다!
| CF | OF | SF | ZF |
|---|---|---|---|
| 1 | 0 |
signed char a=5;
signed char b=2;
if a < b: // cmp a,b → a-b → 5+(-2)
…
| s | ||||||||
|---|---|---|---|---|---|---|---|---|
| 0 | 0 | 0 | 0 | 0 | 1 | 0 | 1 | |
| + | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 0 |
| 0 | 0 | 0 | 0 | 0 | 0 | 1 | 1 |
▽
| CF | OF | SF | ZF |
|---|---|---|---|
| 1 | 0 | 0 | 0 |
SF == 0(=OF의 값)이면 a가 b보다 크다 → SF==OF이면 a가 b보다 크다!
signed char a=2;
signed char b=5;
if a < b: // cmp a,b → a-b → 2+(-5)
…
| s | ||||||||
|---|---|---|---|---|---|---|---|---|
| 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | |
| + | 1 | 1 | 1 | 1 | 1 | 0 | 1 | 1 |
| 1 | 1 | 1 | 1 | 1 | 1 | 0 | 1 |
▽
| CF | OF | SF | ZF |
|---|---|---|---|
| 1 | 0 | 1 | 0 |
SF != 0(=OF의 값)이면 a가 b보다 작다 → SF != OF면 a가 b보다 작다!
| CF | OF | SF | ZF |
|---|---|---|---|
signed char a=100;
signed char b=-100;
if a < b: // cmp a,b → a-b → 100-(-100)
…
| s | ||||||||
|---|---|---|---|---|---|---|---|---|
| 0 | 1 | 1 | 0 | 0 | 1 | 0 | 0 | |
| + | 0 | 1 | 1 | 0 | 0 | 1 | 0 | 0 |
| 1 | 1 | 0 | 0 | 1 | 0 | 0 | 0 |
→ overflow 발생!
| CF | OF | SF | ZF |
|---|---|---|---|
| 0 | 1 | 1 | 0 |
SF==OF 이면 a가 b보다 크다.
signed char a=-100;
signed char b=100;
if a < b: // cmp a,b → a-b → -100+(-100)
…
| 1 | 0 | 0 | 1 | 1 | 1 | 0 | 0 | |
| + | 1 | 0 | 0 | 1 | 1 | 1 | 0 | 0 |
| 0 | 0 | 1 | 1 | 1 | 0 | 0 | 0 |
→ overflow 발생!
| CF | OF | SF | ZF |
|---|---|---|---|
| 1 | 1 | 0 | 0 |
SF!=OF 이면 a가 b보다 크다.
| 플래그 이름 | 기능 설명 |
|---|---|
| CF (Carry Flag) | 덧셈 시 자리올림 또는 뺄셈 시 자리내림이 발생하면 1로 설정됨 |
| PF (Parity Flag) | 결과의 1인 비트 개수가 짝수면 1, 홀수면 0 |
| AF (Auxiliary/Adjust Flag) | 하위 4비트에서 자리올림이 발생하면 1로 설정됨 (BCD 연산에 사용) |
| ZF (Zero Flag) | 연산 결과가 0이면 1로 설정됨 |
| SF (Sign Flag) | 결과가 음수면 1, 양수면 0 (결과의 최상위 비트와 동일) |
| OF (Overflow Flag) | 부호 있는 연산에서 오버플로우가 발생하면 1로 설정됨 |
| DF (Direction Flag) | 문자열 처리 시 주소 증가/감소 방향 제어 (0=증가, 1=감소) |
| IF (Interrupt Flag) | 외부 인터럽트를 허용(1) 또는 차단(0) |
| TF (Trap Flag) | 디버깅용 플래그로, 1이면 명령어 단위로 인터럽트 발생 |
| 규약명 | 인자 전달방식 | 스택 정리 주체 | 특징 |
|---|---|---|---|
| cdecl | 스택(오른→왼) | 호출자(caller) | C언어 기본, 가변인자 지원 |
| stdcall | 스택(오른→왼) | 피호출자(callee) | WinAPI 주로 사용 |
| fastcall | 레지스터+스택 | 피호출자(callee) | 일부 인자 레지스터 사용 |
| thiscall | 레지스터+스택 | 피호출자(callee) | C++ 클래스 멤버함수 |
| x64 System V | 레지스터+스택 | 호출자(caller) | 리눅스 64bit 규약 |
| MS x64 | 레지스터+스택 | 호출자(caller) | 윈도우 64bit 규약 |
Caller와 Callee는 함수 호출 관계에서의 두 주체를 구분하는 개념으로, 호출 규약(Calling Convention)의 핵심 요소
스택 프레임 관리, 레지스터 보존, 반환 경로 설정 등 함수 호출의 정합성을 유지하는 핵심 구조
- Caller
- 다른 함수를 호출하는 함수
- 호출을 준비하고 결과를 받는 주체
- 호출 시 인자를 스택 또는 레지스터에 넣고, 필요한 경우 caller-saved 레지스터를 저장해야 함
- 호출 후에는 callee가 반환한 결과를 지정된 레지스터(보통 RAX 또는 EAX)에서 읽음
- 호출이 끝난 뒤 스택을 정리해야 하는 규약(cdecl 등)에서는 caller가 스택을 복원
- Callee
- 호출된 함수, 즉 실행되는 함수
- 호출되어 작업을 수행한 뒤 결과를 반환하는 주체
- 호출되면 자신의 스택 프레임을 구성하고, 지역 변수나 임시 데이터를 저장할 공간을 확보
- 연산 결과는 일정한 규칙에 따라 반환 레지스터(예: RAX)에 저장
- callee-saved 레지스터의 경우, 함수 실행 중 값을 변경했다면 반환 전에 반드시 원래대로 복원해야 함
- 실행이 끝난 뒤
ret명령으로 caller에게 제어를 반환
| 구분 | 책임 주체 | 예시(x86-64 Linux) | 설명 |
|---|---|---|---|
| Caller-saved | Caller | RAX, RCX, RDX, R8–R11 | 함수 호출 시 변동 가능. 필요 시 caller가 직접 백업 필요 |
| Callee-saved | Callee | RBX, RBP, R12–R15 | 함수 내에서 사용 가능하되, 변경 시 반드시 원래 값 복원 |
a.ca.c 코드 작성gcc a.c로 실행 파일을 만듦a.out 같은 실행 파일이 됨gcc -o myfile a.c처럼 -o 옵션을 사용./a.out./는 "현재 디렉토리"를 의미하고, 뒤의 a.out은 그 디렉토리에 있는 실행 파일을 실행하라는 뜻a.out 파일을 분석할 수 있음#include <stdio.h>
void foo()
{
printf("foo()\n");
}
int main()
{
foo(); // call foo
}
컴파일이란 "사람이 작성한 소스코드(C, C++, Java 등 고급 언어)"를 컴퓨터가 직접 실행 가능한 "기계어(실행 파일, .exe, .out 등)"로 변환하는 과정입니다.
- 컴파일 과정 4단계
- 전처리(Preprocessing) : #include, #define 등의 지시문을 처리하고, 헤더 파일 삽입, 주석 제거, 매크로 치환 작업을 합니다. 결과는 .i 파일에 저장됩니다.
- 컴파일(Compilation) : 전처리된 코드를 어셈블리어(.s)로 변환합니다. 문법 검사와 의미 분석이 이루어지며, 오류를 체크합니다.
- 어셈블(Assembly) : 어셈블리어 파일을 목적 파일(.o)로 변환합니다. 이는 CPU가 이해할 수 있는 명령어 집합으로 작성된 기계어.
- 링킹(Linking) : 목적 파일(.o)과 필요한 라이브러리 파일을 합쳐서 하나의 실행 파일(.exe, .out 등)로 만듭니다.
- 핵심 의미
- 사람이 쓴 코드를 컴퓨터가 실행할 수 있는 형태로 만드는 과정이 바로 컴파일입니다.
- C컴파일러(gcc), C++컴파일러(g++), 자바컴파일러(javac) 등 여러 종류가 있으며, 각 언어별로 조금씩 과정이 다릅니다.
- 컴파일이 끝나면, 컴퓨터는 소스코드가 아닌 실행 파일을 직접 읽어서 동작합니다.
objdump -d a.out : 실행 파일의 “실행 가능한 코드 부분”을 어셈블리(기계어에 가까운 명령어)로 보여줘 foo, main 같은 함수가 실제 어떤 명령어로 구현되어 있나 볼 수 있음objdump -h a.out : 바이너리 내 각 “section”(코드, 데이터, 심볼 등)의 크기와 위치 정보를 보여줌objdump -x a.out : 헤더·섹션 정보를 포괄적으로 보여줌objdump -s a.out: 바이너리 각 섹션의 원본 “hex”(16진수) 데이터를 보여줌소스코드만 보면 추상적인 함수 호출이, 실제로는 call, ret와 같은 명령의 연속, 주소점프(JMP) 등으로 상세하게 작동함을 볼 수 있습니다
출력 결과를 깊이 이해하면, 컴파일러 동작, 최적화, 시스템 내부 원리, 보안 분석, 디버깅 등에 큰 도움이 됩니다.
int x = 2 + 3 * 4; → 실행파일에는 x = 14;로만 저장됨. 실제 계산 명령은 없음.#include <stdio.h>
void foo()
{
printf("foo()\n");
}
int main()
{
foo; // foo → 1149
}
반환형 함수이름(매개변수) {
// 함수 몸체
return 반환값;
}
int sum(int a, int b) {
return a + b;
}
→ sum이 함수 이름이고, int a, int b가 argument, int가 return type
#include <stdio.h>
void foo()
{
printf("foo()\n");
}
// *p++ : 전치와 후치가 만나면 후치부터 묶는다
// (*p)++로 써야 원하는 방식으로 작동함
int main()
{
void (*a)() = foo; // foo → 1149
a();
}
gcc, g++
gcc와 g++는 모두 GNU에서 제공하는 컴파일러이지만, "지원하는 언어"와 "링킹 방식"에 주요 차이가 있음
- 핵심 차이점
- gcc: "C 언어"를 기본적으로 지원하며, .c 파일은 C로, .cpp 파일은 C++로 각각 컴파일
- C 코드 컴파일에 최적화되어 있고, 기본적으로 C 라이브러리만 링크합니다. C++ 라이브러리는 자동적으로 링크되지 않음
- g++: "C++ 전용" 컴파일러로 .c와 .cpp 파일을 모두 C++ 언어로 컴파일하며, C++ 표준 라이브러리까지 자동으로 링크
- 실질적 의미
- C 코드, .c 파일: gcc 사용이 기본이며, g++도 가능하나 내부적으로 C++ 방식으로 처리됨
- C++ 코드, .cpp 파일: g++ 사용이 안정적이며 권장됨. gcc로 컴파일 시 C++ 라이브러리 미링크로 인해 에러가 날 수 있음
# if 1
#include <stdio.h>
void foo()
{
printf("foo()\n");
}
int main()
{
foo();
}
# endif
# if 0
#include <stdio.h>
void foo()
{
printf("foo()\n");
}
int main()
{
foo; // 함수 이름만 쓰면, "함수의 주소"—즉, 함수 코드가 시작하는 메모리 위치—를 의미
}
# endif
#if, #endif, #if 0, #if 1은 “전처리기(preprocessor)”가 어떤 코드 블록을 컴파일할지 선택하는 역할#if DEBUG#if defined(WINDOWS)#define USE_LOG 1 #if USE_LOGjmp가 call로 바뀐 이유는 프로그램의 제어 흐름을 함수 호출 방식으로 변경하기 위해서입니다. jmp는 단순히 프로그램의 위치를 무조건 이동시키는 반면, call은 함수를 호출하고 실행이 끝난 후 원래 위치로 돌아갈 수 있도록 복귀 주소(Return IP)를 스택에 저장한 후 함수로 점프합니다. 따라서 call은 함수 실행 후 원래 코드로 복귀해야 할 경우에 사용됩니다.
| PC |
|---|
| 1170 |
0000000000001149 <foo>:
1149: endbr64
114d: push %rbp
114e: mov %rsp,%rbp
1151: lea 0xeac(%rip),%rax # 2004 <_IO_stdin_used+0x4>
1158: mov %rax,%rdi
115b: call 1050 <puts@plt>
1160: nop
1161: pop %rbp
1162: ret
0000000000001163 <main>:
1163: endbr64
1167: push %rbp
1168: mov %rsp,%rbp
116b: mov $0x0,%eax
🡆 1170: call 1149 <foo>
1175: mov $0x0,%eax
117a: pop %rbp
117b: ret
| PC |
|---|
| 1149 |
0000000000001149 <foo>:
1149: endbr64
114d: push %rbp
114e: mov %rsp,%rbp
1151: lea 0xeac(%rip),%rax # 2004 <_IO_stdin_used+0x4>
1158: mov %rax,%rdi
115b: call 1050 <puts@plt>
1160: nop
1161: pop %rbp
1162: jmp 1175
0000000000001163 <main>:
1163: endbr64
1167: push %rbp
1168: mov %rsp,%rbp
116b: mov $0x0,%eax
1170: jmp 1149 <foo>
1175: jmp 1149 <foo>
117a: pop %rbp
117b: jmp
→ jump 쓰면 loop가 됨
| eax |
|---|
| 1175 |
| PC |
| 1149 |
0000000000001149 <foo>:
1149: endbr64
114d: push %rbp
114e: mov %rsp,%rbp
1151: lea 0xeac(%rip),%rax # 2004 <_IO_stdin_used+0x4>
1158: mov %rax,%rdi
115b: call 1050 <puts@plt>
1160: nop
1161: pop %rbp
1162: jmp %eax
0000000000001163 <main>:
1163: endbr64
1167: push %rbp
1168: mov %rsp,%rbp
116b: mov $0x0,%eax
1170: jmp 1149 <foo>
1175: jmp 1149 <foo>
117a: pop %rbp
117b: jmp
eax 레지스터는 x86 CPU에서 가장 중요한 범용 레지스터 중 하나로, 주로 산술 연산(더하기, 빼기, 곱하기, 나누기 등)의 결과를 저장하거나 함수의 반환값을 담는 데 사용
CPU가 “즉시 접근”하고 “초고속으로 데이터 처리”할 수 있는 공간이 바로 EAX 같은 레지스터
프로그램 내부 어셈블리에서는 함수 반환, 계산 결과, 중간값 저장 등 다양한 목적으로 활용
- 크기: 32비트(4바이트)
- 하위 16비트는 AX, 그 아래는 AL/AH로 구분 가능
- x86-64에서는 RAX로 확장되어 64비트
- 하지만 32비트 프로그램에서는 EAX로 사용
- 용도
- 산술·논리 연산에 최적화되어 있어서, 연산 결과가 자동으로 EAX에 저장되는 경우가 많음
- 함수 실행 후 반환값을 전달하거나, 시스템 호출 같은 특수 연산에서도 결과값을 담는 컨벤션에 이용
mov eax, 5 // EAX에 5 저장 add eax, 3 // EAX에 3 더함 (결과: 8) ret // 함수 반환 시 EAX에 반환값 들어있음실제로 C에서 int 함수 반환 → eax에 값 저장
def bar():
pass
def foo():
bar()
foo()
| eax |
|---|
| 1175 |
| PC |
| 1149 |
rsp 위치를 잘 따라가보자
+-------+
| rsp | # rsp(Stack Pointer): 현재 1175를 가리킴
+-------+
| 1175 | # main에서 push됨 (리턴 주소 등)
+-------+
| 1160 | # foo에서 push됨
+-------+
| . | # 아직 사용되지 않은, 혹은 이후 추가될 공간
+-------+
| . |
+-------+
| . |
+-------+
| . |
+-------+
| . |
+-------+
rpb 사용| 명령어 | 기능 | 특징 |
|---|---|---|
| jmp | 지정한 주소로 단순히 점프 | 되돌아올 주소를 저장하지 않음 → 복귀 불가능 |
| call | 함수 또는 서브루틴을 호출 | 복귀할 주소를스택에 push후 점프 →ret명령으로 복귀 가능 |
jmp만으로는 “돌아올 길이 없는 흐름 제어” 문제를 해결할 수 없기 때문에, CPU는 스택을 사용해 복귀 주소를 저장하는 call/ret 체계를 도입 → 현대적인 함수 호출, 스택 프레임, 재귀 함수 호출 등의 기반 마련
| 레지스터 | 역할 |
|---|---|
| SP (Stack Pointer) | 현재 스택의 최상단 위치를 가리킴. push/pop으로 값이 변함 |
| BP/FP (Base Pointer / Frame Pointer) | 현재 함수의 스택 프레임 시작점을 가리킴. 지역변수와 매개변수를 참조할 기준점으로 사용됨 |