Chapter2
Data race와 Atomic Operation의 원리
Data race (데이터 경쟁)

- 공유된 위치에 두 개의 동시 접근이 발생하고, 그 중 적어도 하나의 접근이 쓰기 작업을 포함할 때
Atomic Operations (원자적 연산)


- 메모리 위치 주소에서 하나의 ISA 명령어로 수행됨
- 이전의 값을 읽음
- 새 값을 계산함
- 새 값을 해당 위치에 씀 => 이 순서대로!!
- 하드웨어는 원자적 연산이 완료될때까지 다른 thread가 해당 위치에 접근하지못하게 함
- 해당 위치에 접근하려는 다른 thread는 대기열에서 차례를 기다림
- 모든 thread는 원자적 연산을 직렬로(serially) 수행
Translation and Startup

C프로그램이 컴파일되고 실행파일로 생성된 후 메모리에서 실행되는 과정!
- C Program
사람이 읽을 수 있는 고급언어(HLL)
⬇
(compiler): 어셈블리 수준의 언어 프로그램으로 변환하는 과정 수행
⬇
- Assembly language program
⬇
(Assembler): 어셈블리 언어 프로그램을 기계어 모듈(==오브젝트 파일)로 변환
⬇
- Object: Machine language module, Object: Library routine(machine language)
⬇
(Linker): 기계어로 작성된 오브젝트 파일과 라이브러리나 다른 모듈을 결합하여 실행 파일을 완성시킴
⬇
- Executable: Machine Language program
실행파일 -> 기계어 프로그램
⬇
(Loader): 최종적으로 완성된 실행 파일을 메모리로 로드하는 역할
⬇
- Memory
- 많은 컴파일러는 직접적으로 오브젝트 모듈을 생성함
- Static linking(정적 링크): 모든 필요한 코드가 하나의 실행 파일에 결합되어 외부 라이브러리 없이 독립적으로 실행될 수 있도록 함
Array(배열) vs Pointer(포인터)
배열
- 배열 인덱싱은 각 접근마다 인덱스를 요소 크기와 곱해야함
- 배열의 기본 주소에 더함
뜻: 배열의 각 인덱스에 접근할 때는 배열의 기본 주소(base address)에 인덱스 요소 크기를 더해 메모리 위치를 계산, ex. arr[2]에 접근하려면 배열의 기본 주소에 2 4(4바이트는 int 크기)를 더해 실제 메모리 위치를 계산
포인터
- 메모리 주소에 직접 대응하여 인덱싱의 복잡함을 피할 수 있음
배열과 포인터의 비교
- 배열 -> 포인터로 바꾸면, 곱셈이 shift로 바귀어 강도가 감소함!
- 배열은 루프 내부에서 shift가 필요함
- 증가하는 i에 대한 인덱스 계산의 일부
- cf. 포인터를 증가하는 것과 비교
- 컴파일러의 효과 == 포인터를 수동으로 사용하는 것의 효과
- 귀납 변수(induction variable)를 제거함 (반복문의 i++를 제거)
- 프로그램을 더 명확하고 안전하게 만듦
(즉, 컴파일할때 배열로 작성되어 있어도 다 포인터로 바꾼다는 뜻)
Fallacies and Pitfalls
<instruction의 중요성 알기>
- 강력한 명령어 == 더 높은 성능
- 적은 명령어가 필요함 (메모리를 적게 쓰기 위해)
- 복잡한 명령어는 구현하기 어려움
- 단순한 명령어를 포함한 모든 명령어를 느리게 만들 수 있음
- 컴파일러는 단순한 명령어로부터 빠른 코드를 작성하는 데 능숙함
- 높은 성능을 위해 어셈블리 코드를 사용
- 현대 컴파일러는 최신 프로세서를 더 잘 처리함
- 코드 라인이 많아질수록 오류가 증가하고 생산성이 감소함

- 명령어 집합은 변하지 않음 => 하위 호환성
- 시간이 지남에 따라 명령어가 더 추가되기는 하지만(확장), 새 시스템이나 소프트웨어가 이전 버전에서 사용된 명령어 집합을 그대로 유지함으로써 기존과 호환될 수 있도록 함
Chapter3: Arithmetic for Computer(컴퓨터의 산술 연산)
정수 연산
정수 덧셈(Integer Addition)

- 오버플로우: 결과가 범위를 벗어날 경우 발생
- 양수 + 음수 => 오버플로우 발생 x
- 양수 + 양수 => 계산 결과에서 부호(sign) 비트가 1인경우 오버플로우 발생한 것
- 32비트 정수에서 최대값 + 10 => 음수로 넘어감
- 음수 + 음수 => 계산 결과에서 부호(sign) 비트가 0인경우 오버플로우 발생한 것
- 32비트 정수의 거의 최솟값 + -10 => 양수로 넘어감
정수 뺄셈(Integer Subtraction)

- 정수 뺄셈: 두 번째 피연산자(operand)의 부호를 반전시켜서 더하는 방식으로 수행
- 오버플로우: 결과가 범위를 벗어날 경우 발생
- 양수 - 양수, 음수 - 음수 => 오버플로우 발생 x
- 음수 - 양수 => 부호(sign) 비트가 0인경우 오버플로우 발생한 것
- 양수 - 음수 => 부호(sign) 비트가 1인경우 오버플로우 발생한 것
+) 2의 보수 구하는 방법
1. 이진수의 모든 비트를 반전시킴 -> 1의 보수
2. 1을 더함
- ex. 6의 2의 보수
- 6을 4bit 2진수로 표현하면, 0110
- 1의 보수 => 1001
- 1 더하기 => 1010
정수 곱셈 (Integer Multiplication)

- 긴 곱셈(long-multiplication) 방법을 사용하여 수행
- 곱셈 과정은 10진수(deximal) 곱셈과 동일한 방식으로 수행
- 각 비트에 대해 곱셈을 수행 -> 부분 곱에 더해줌
- 이진수 => A와 B의 곱셈은 AND 연산으로 수행 (둘다 1일때만 1)
- 곱셈의 결과 길이 == 피연산자의 길이 합 (4자리 + 4자리 = 8자리, 결과도 8자리)
🌟Floating Point
- 정수가 아닌 숫자를 표현하기 위한 방법 (매우 작은수, 매우 큰 수를 모두 표현할 수 있음!)
- 과학적 표기법과 유사함
- 10진수 기본 골자 => ±0.00 × 10ⁿ를 꼭 지켜야함!
- 2진수 기본 골자 => ±1.xxxxxxx × 2ⁿ(여기서 n은 4개)
- c언어에서는 float와 double로 사용됨
부동소수점 표준(Floating Point Standard)
- IEEE Std 754-1985에 의해 정의됨
- 다양한 부동소수점 표현의 차이로 인해 개발된 표준으로, 과학 계산용 코드의 이식성 문제 해결 위해 만들어짐!
- 현재는 전 세계적으로 채택됨!
- 두 가지 표현방식
- Single Precision (32-bit, 단정밀도)
- Double Precision (64-bit, 배정밀도)

- S(부호): Sign bit
- 정규화된 가수(normalize significand): 1.0 ≤ |significand| < 2.0
- 항상 1이 선행하는 비트이므로, 이를 명시적으로 표현할 필요x (== 숨겨진 bit)
- 가수는 분수(fraction)으로 표현되고 "1."은 연산시 복원됨
- Exponent(지수): 초과 표현방식으로, 실제 지수 값 + Bias로 표현됨
- Single precision(단정밀도): Bias = 127
- Double precision(배정밀도): Bias = 1023
Example

