2. 컴퓨터 하드웨어의 최적화
- 왜 최적화가 필요한지 알 수 있다.
- 프로세서의 구조를 통해서 어떤 방법으로 최적화해야 하는지 유추하는 방법을 알아봄.
C++은 컴퓨터의 거짓말을 믿습니다.
- C++ 프로그램은 '마치' 명령문을 순서대로 실행하는 것처럼 작동하기만 하면 됩니다.
- C++ 컴파일러와 컴퓨터 자체는 계산의 의미가 변경되지 않은 선에서 실행순서를 변경하여
- 프로그램 실행을 빠르게 할 수도 있습니다.
C++11부터는 단일 실행 주소만 있다고 믿지 않습니다.
- 이제 C++표준 라이브러리는 스레들 시작 및 중지하고 스레드 간에 메모리 접근을 동기화하는 기능을 제공한다.
- 특정 메모리 주소는 일반 메모리가 아닌 장치 레지스터가 될 수 있다.
- 동일한 스레드가 두 번 연속해서 읽는 사이에 변할 수 있으며,이는 하드웨어에 일부 변경이 있었음을 나타낸다.
- C++에서는 이러한 위치를 volatile로 나타내어 최적화하는 대신 컴파일러가 변수의 새 복사본을 가져온다.
* volatile 레지스터 참조 변수
C++11은 std::atomic<>이라는 마법 주문을 제공합니다.
- std::atomic<>은 메모리를 잠시 동안 마치 단순한 선형 바이트 저장소인 것처럼 작동하게 만들고,
- 멀티스레드 실행, 멀티 레이어 메모리 캐시 등으로 현대 마이크로프로세서의 모든 복잡성을 제거하려고 한다.
*voliatile이 하고 있는 일은 아닙니다.
운영체제도 프로그램과 사용자에게 거짓말을 합니다.
- 실제로 운영체제의 모든 목적은 각 프로그램에게 납득할 거짓말을 하는 것입니다.
1. 프로그램이 컴퓨터에서 단독으로 사용되고,
2. 물리 메모리는 무한하며,
3. 프로그램의 스레드를 실행 할 수 있는 프로세서가 무한이다.
- 이러한 거짓말은 프로그램을 느리게 실행할 때를 제외하고는 큰 영향을 미치지 않는다.
컴퓨터의 진실
- 메모리는 느립니다.
1. 메인 메모리는 마이크로프로세서에 있는 게이트와 레지스터보다 매우 느리다.
2. 메모리에 접근하는 비용은 프로세서의 다른 비용들을 압도한다.
* 폰노이만 현상
- 메모리와 CPU사이에 BUS가 하나이면서 순차적으로 정보를 처리하기 때문에 성능에 제한이 걸린다.
* BUS메모리와 CPU사이의 연결 통로
- 메모리는 워드 단위로 접근합니다.
1. 정렬되지 않은 메모리 접근의 단점 :
C++에서 int, double, 포인터처럼 여러 바이트를 갖는 자료형을 가져 올 때,
해당 데이터를 구성하는 바이트가 실제 메모리에서는 두 워드에 걸쳐 있을 수 있다.
정렬되지 않은 접근은 모든 바이트가 같은 워드에 있을 때보다 시간이 2배로 걸린다.
2 .정렬된 메모리 접근의 단점 :
사용하지 않는 데이터가 구조체에 포함될 수 있다.
- 메모리마다 접근 속도가 다르다.
1. 메모리 마다 속도가 다르고 어떤 컴퓨터는 캐시 메모리가 여러단계로 이루어져 있다.
* 캐시메모리도 10배 이상 속도차이가 날 수 있다.
2. 캐시메모리는 빠를수록 용량이 적기 때문에 실행유닛에서
캐시에 없는 데이터를 가져와야 하는 경우에 현재 캐시에 있는 데이터 중 일부를 삭제해야 합니다.
3. 1바이트만 읽어도 근처에 있는 바이트가 함께 캐싱
이러한 정책은 적게 사용하는 메모리 위치보다 많이 사용하는 메모리 위치에 더 빠르게 접근할 수 있다.
인접한 위치의 메모리가 멀리 떨어진 곳의 메모리보다(평균적으로) 더 빨리 접근 가능
ex) 반복문 코드 블록에 사용된다.
ex) if문은 코드 블록이 서로 근처에 있지 않기 때문에 오래 걸린다.
4. 워드를 저장하는 방법에는 빅 엔디언과 리틀 엔디언이 있다.
메모리를 할당 할 때, 첫 번째 바이트를 최상위 비트로 정할 것인가? 최하위 비트로 정할 것인가?
* 빅 엔디언 : 최상위 비트로 정함
* 리틀 엔디언 : 최하위 비트로 정함
디스크와 네트워크는 한 번에 1바이트 씩 보내기 때문에
데이터를 디스크에 기록하거나 네트워크를 통해 전송할 때 중요하다.
* 보내는 컴퓨터와 받는 컴퓨터의 엔디언이 일치하지 않으면 서로 다른 값을 받게 됨
5. 메모리는 한정된 자원이며 캐시메모리의 부족으로 가상메모리를 사용하게 된다.
가상메모리가 캐시메모리 보다 크다.
* 가상메모리는 디스크에서 블록을 검색하는데 수십 밀리초가 걸린다.
여러 메모리 위치에서 분산 접근을 하는 경우 캐시메모리에 데이터가 없을 수 있다.
* 페이지 스레싱이 일어난다.
* 페이지 스레싱 : CPU보다 페이지폴드로 처리하는데 시간을 다 할애하는 것
6. 명령 실행은 느리다.
파이프라인이 복잡해질수록 명령들을 한꺼번에 처리 할 수 있지만,
명령 A가 명령 B에 필요한 값을 계산다한다고 하면, 명령 A가 완료될 때까지 멈출 수 밖에 없게 된다.
7. 컴퓨터는 의사 결정을 잘 하지 못합니다.
실행의 흐름을 변경하는 명령(점프, 함수호출 ..)을 처리하는 동안 실행주소가 갱신될 때까지
메모리에서 잠시 동안 읽을 수 없고, 파이프라인에 놓일 수 없다.
* 계산이 의사결정보다 빠르다.
8. 프로그램 실행에는 여러 스트림이 있습니다.
운영체제는 디스크, 인터페이스, 사운드 등 여러가지 장치와 연결되어 있고, 자원을 사용하기 위해 서로 경쟁한다.
콘테스트 스위칭이 일어나고 상당한 비용이 든다.
9. 운영체제 기능을 호출하는 비용은 높습니다.
시스템 호출 비용이 프로그램 내에 함수 호출비용보다 훨씬 더 크다.
C++도 거짓말을 합니다.
- 구조체를 전체를 복사하는데, 단순히 멤버 변수만 복사하면 된다고 생각하지만 복잡한 임의의 식이 정해질 수 있다.
* 문장의 형태만으로는 비용이 얼마나 드는 알 수 없다.
- 문장은 순서대로 실행되지 않습니다.
- 컴파일러는 성능을 향상하려고 내부에서 문장을 재정렬하고 때로는 순서를 변경한다.
- 컴파일러는 다중 스레드를 고려하지 않기 때문에 명시적인 동기화 코드를 추가개햐 한다.
- 동기화 코드는 동시에 실행 될 수 있는 스레드가 데이터를 공유함으로써 얻을 수 있는 동시성의 양을 줄입니다.
'Language > C++최적화' 카테고리의 다른 글
C++최적화 (1단원) (0) | 2021.09.17 |
---|
댓글