2020 이전/C++

CPU도 구라를 치고 C++도 구라를 친다

이상해C++ 2019. 8. 8. 02:00

최적화에서 중요한점

1. 실제 컴퓨터의 메모리 하드웨어는 명령 실행 속도보다 매우 느리다

2. 메모리는 byte 단위로 접근 되지 않는다. + 유한한 용량을 갖는다.

3. 프로세서는 하나의 명령어를 Fetch->Decode->Execute->결과 반영 의 단계를 나누는데, CPU는 단계수만큼 명령어를 동시에 실행할 수 있다. 1번 명령어가 명령어 해석(Decode)로 들어가면 2번 명령어가 Fetch단계에 진입하는 방식으로 CPU는 두개 이상의 명령어 주소를 가지고 있을 수 있다. 실제로 여러개의 명령을 수행하지만 결과 반영을 순차적으로 해주는것때문에 명령이 순차적으로 실행되는 것처럼 작동되어 보이는것-> 대표적인 프로세서의 구라인데 멀티스레딩에서  자기 자신의 프로그램은 순서대로 실행되는 것처럼 보이나 옆(다른 코어)에서 봤을 때 어, 저 코어 구라를 치는데?라고 보인다. 가 이 이유

 

3 비 순차적 명령어 처리 : https://ko.wikipedia.org/wiki/%EB%B9%84%EC%88%9C%EC%B0%A8%EC%A0%81_%EB%AA%85%EB%A0%B9%EC%96%B4_%EC%B2%98%EB%A6%AC

메인 메모리의 비극과 구라

1. 메인 메모리는 속도가 느리다.

메인 메모리는 프로세서가 명령어를 처리하는 것보다 속도가 매우 느리다. 그렇기 때문에 메모리에 접근하는 비용은 프로세서의 다른 비용을 압도한다. 메인 메모리에서 속도가 제한 되는 현상을 폰 노이만 병목현상이라고 한다.

 

2. 메모리는 워드 단위로 접근한다

여러 바이트를 가지고있는 자료형의 데이터에 접근했을때, 해당 데이터가 두 워드에 걸쳐있을 경우(정렬 되지 않은 메모리 접근) 시간이 2배로 걸린다. 한워드가 아니라 두개의 워드를 메모리에서 가져와야하기 때문이다. 

C++컴파일러는 구조체 내 각 필드의 시작 주소가 필드 크기의 배수인 바이트 주소가 되도록 Byte Align한다. 

 

3. 메모리마다 접근속도가 다르다.

속도: L1/L2/L3 캐시 메모리 > 메인 메모리 > 디스크상 가상 메모리 페이지 

캐시 메모리 적중률이 높을수록 빠르다. 캐시 메모리는 보통 근처에 있는 바이트가 함께 캐싱 된다. 인접한 위치의 메모리가 멀리 떨어진 메모리보다 평균적으로 더 빨리 접근할 수 있다. -> 배열, 벡터 자료구조는 연속된 공간에 존재하기 떄문에 링크드 리스트와 같은 자료구조보다 접근 속도가 빠르다.  

 

4. 빅 엔디안, 리틀 엔디안

0x01234567

메모리 번지 1000 1001 1002 1003
빅 엔디안 0x67 0x45 0x23 0x01
리틀 엔디안 0x01 0x23 0x45 0x67

빅 엔디안: 첫번째 바이트 주소에 최상위 비트를 먼저 저장

리틀 엔디안: 최하위 비트를 먼저 저장

 

5. 메모리는 무한하지 않다

가상메모리: 물리 메모리가 더 많이 있다는 환상-> 디스크에서 메모리 블록을 가져오므로 시간이 매우 느리다

메인메모리는 ns 단위라면 가상 메모리는 디스크에서 수십ms 단위로 걸린다. 

페이지 스래싱(CPU가 실제 작업을 수행하는 시간보다 페이지 폴트를 처리하는데 걸리는 시간이 더 많은 것)이라도 일어나는 날에는? Oh... 

 

6. 명령어 Execution은 느리다

파이프라인 스톨: 선행되어야하는 명령때문에 다른 명령이 수행이 되지않는 현상

비순차적 명령어 처리 프로세서에서는 파이프라인 스톨 현상이 일어나 느려질 수 있다. 

 

7. 와리가리 컴퓨터

보통 다음 명령어는 캐시 메모리에 존재한다. 하지만, jump나 function call같이 transfer-of-control 명령어의 새 실행주소의 메모리 워드는 캐시에 있을 가능성이 적다. 실행 주소가 갱신되고 새 다음 명령을 파이프라인으로 다시 불러오는 동안 파이프라인이 스톨된다. 

 

8. 그놈의 컨텍스트 스위칭

스레드끼리의 컨텍스트 스위칭

멀티 스레딩 프로그래밍에서 컨텍스트 스위칭 일어나는 경우, 현재 실행하던 스레드의 프로세서 레지스터 상태를 저장하고 실행할 스레드의 레지스터 상태를 불러온다. 새 스레드가 실행이 재개되면, 해당 데이터가 캐시에 저장되어있지 않을 수 있으므로 캐시로 불러오는 비용도 고려하면, 컨텍스트 스위칭의 비용은 비싸다.

 

프로세스끼리의 컨텍스트 스위칭

모든 더티 캐시 페이지(기록된 데이터가 메인 메모리에 도달하지 않은 페이지)는 물리 메모리로 전송되어야한다. 모든 프로세서 레지스터가 저장된다. 메모리관리자의 물리-가상 메모리 페이지 레지스터가 저장된다. 그 뒤 새 프로세스의 물리-가상 메모리와 프로세서 레지스터를 불러와야한다. 스레드간 컨텍스트 스위칭보다 비용이 높다. 

 

멀티 코어 프로세서에서 CPU 연관 캐시 메모리는 성능때문에 독립적으로 작용하지만, 모든 실행 유닛은 메인 메모리를 공유한다. 

 

9. 운영체제 기능을 호출하는 비용은 크다.

OS 커널은 프로세스가 운영체제에 system call을 요청할수 있도록 모든 메모리에 접근할 수 있어야한다. 시스템 호출비용은 한 프로그램 단일 스레드 내에서 함수 호출보다 수백배 더 크다.

C++의 구라

1. 문장의 비용은 같지않다.

int 4백개 들어있는 클래스의 복사생성자를 낀 대입연산자의 비용과 단일 int의 대입연산자의 비용은 같지 않다.

 

2. 문장은 순서대로 실행되지 않는다.

컴파일러는 성능을 향상하기 위해 내부에서 문장을 재정렬하고 때로는 순서를 변경한다. 멀티스레드 프로그래밍 환경에서 컴파일러는 스레드간 공유되는 변수가 있는지 모른다. 이 변수를 컴파일러가 최적화 한다면?

-> 명시적인 동기화를 걸어야한다.

 

 

<참고>

Optimized C++ 2장

 

'2020 이전 > C++' 카테고리의 다른 글

서버 공부 자료 링크  (0) 2019.10.14
vector대 list  (0) 2019.10.11
거부할 수 없는 너의 흑마법은 멀티스레딩  (0) 2019.08.05
해시 버켓 사이즈 비교 실험  (0) 2019.07.06
C++의 까다롭고 유별난 친구들2  (0) 2019.03.22