관리 메뉴

기억을 위한 기록들

[CPP] C++ 최적화에 관하여 (2-3) 본문

C & CPP

[CPP] C++ 최적화에 관하여 (2-3)

에드윈H 2021. 8. 16. 15:40

7. 문장 최적화

 - 문장 수준에서 최적화할 때, 문장의 비용을 크게 만드는 요인이 없다면 성능이 생각만큼 개선되지 않습니다.

 - 반복문에서 문장의 비용은 반복된 횟수만큼 커진다.

1. 반복문의 종룟값을 캐싱하기
2. 경우에 따라 for문보다 do while문이 더 효율적일 수도 있다.
3. for에서 값 증가보다 감소가 아주 쪼금 더 빠르다.(일부 컴파일러)
4. 반복문에서 불변 코드는 반복문 밖에서 계산하기
5. 반복문에서 불필요한 함수 호출을 제거하시오. 반복 문안에 있는 함수의 호출을 매번 호출해야 하는지 살펴보자.
6. 반복문에서 숨겨진 함수호출을 제거하시오. 대부분 클래스 타입의 변수다. 생성자 소멸자 대입 등이 일어날 때 숨겨진 함수 들일 호출된다. 
7. 반복문에서 비용이 크고 변화가 느린 호출을 제거하시오.
8. 반복문을 함수 안에 넣어 호출 오버헤드를 줄이기.
9. 어떤 행동을 하는 횟수 줄이기

 - 함수에서 문장의 비용은 함수가 호출된 횟수만큼 커진다.

함수는 두부분으로 본문인 코드 블록과 인수 목록/반환 타입이 있는 함수의 헤드 부분으로 나뉜다. 두 부분은 따로따로 최적화할 수 있다.

 - 자주 사용하는 관용구의 비용은 관용구가 사용된 횟수만큼 커진다.

 - C++ 문장 중 일부에는 숨겨진 함수 호출이 포함되어 있다. (ex. 생성자/대입연산자/ 연산자 등등...)

 - 운영체제를 호출하는 함수 코드는 비용이 많이 든다.

 - 함수 호출로 발생하는 오버헤드를 효과적으로 제거하는 방법은 함수를 인라인 하는 것이다.

vs에서 디버그 빌드와 릴리스 빌드의 성능 차이가 나는 이유는 기본적으로 디버그 빌드에서는 함수 인 라이닝을 끄기 때문이다.

 - 표현식은 단순하게 만들어라. 개발자가 작성한 표현식이 예상 표현 범위를 갖는 인수를 통해 정확한 결과를 산출한다고 알고 있어야 한다. 컴파일러는 이 작업을 도울 수 가 없다.

 - 부동 소수점 연산 대신 정수 연산 사용하자. 그리고 double이 float보다 빠를 수도 있다. (정수를 계산하는 속도가 부동 소수점보다 계산하는 속도가 최소 10배는 빠르다. double이 float보다 같은 계산이 2배가 빠를수도 있음.)

 

 

 

 

 

 

8. 라이브러리 최적화

- 함수와 클래스는 다른 방법으로 제공할 수 없거나 여러 운영체제에서 매우 재사용할 수 있기에 C++라이브러리에 들어가 있습니다.

- 표준 라이브러리의 구현에는 버그가 있다.

없다고 생각하면 안 된다

- '표준을 준수하는 구현' 같은 건 없다

- 표준 라이브러리의 개발자에게 가장 중요한 건 성능이 아니다.
- C++ 표준 라이브러리의 모든 부분이 똑같이 유용하지는 않다.

- 표준 라이브러리는 운영체제의 가장 좋은 네이티브 함수만큼 효율적이지 않다.

- 기존 라이브러리를 갱신할 때는 가능한 한 적게 수정하라.

변경하기보다는 추가하는 게 나을 수도

 

- 인터페이스의 안정성은 라이브러리의 핵심 산출물이다.

- 테스트 케이스는 라이브러리 최적화에 중요하다.

- 라이브러리를 설계하는 것은 다른 C++ 코드를 설계하는 것과 같다. 다만 더 위험할 뿐

- 추상화에서 클래스의 파생 계층은 대부분 3 계층 이하여야 한다.

최적화 관점에서 보면 상속 계층 구조가 깊어질수록 멤버 함수를 호출할 때 계산을 추가로 수행할 확률이 높아진다.

- 추상화 구현에서 함수 호출은 대부분 중첩 횟수가 3회 이하여야 한다.

 

9. 검색 및 정렬 최적화

1. 기존 구현 코드의 성능을 측정하고 비교하기 위한 기준치를 얻는다.

2. 최적화할 추상화 코드를 확인.

3. 최적화할 코드를 알고리즘과 자료구조로 분해. (어떤 알고리즘과 어떤 자료구조를 사용할지)

4. 최적이 아닌 알고리즘이나 자료구조를 바꾼다.

 

선형 검색->이 진검 색-> 해싱일수록 빠름.

 

10. 자료구조 최적화

컨테이너 클래스마다 멤버 함수의 이름이 같더라도 컨테이너마다 문법이 다르다.

 

- 시퀀스 컨테이너 : std::string/std::vector/std::deque/std::list/std::forward_list 

시퀀스 컨테이너는 순서대로 항목을 뒤 삽입(push_back).

효율적으로 앞에 삽입 가능한 건 std::deque/std::list/std::forward_list 뿐이다.

string/vector/deque는 내부적으로 배열과 같은 구조이다. 

- 연관 컨테이너 : std::map/std::multimap/std::set/std::multiset

순서대로 저장하지 않고, 항목의 순서 특성에 기반하여 저장.

선형 시간 이하의 복잡도를 갖는다.

연관 컨테이너는 동일한 자료구조인 이진트리를 퍼사드 패턴으로 구현.

- std::vector는 삽입/삭제/반복/정렬이 가장 빠른 컨테이너이다.
- 정렬된 std::vector에서 std::lower_bound를 사용한 검색은 std::map과 경쟁할만하다.
- std::deque는 std::list보다 약간 빠르다.
- std::forward_list는 std::list보다 빠르지 않다.
- 해시 테이블 std::unordered_map은 std::map보다 빠르다. 하지만 해시 테이블의 명성에 비해 엄청나게 빠르지 않다.
- 컨테이너 클래스의 Big-O 성능이 모든 사실을 말해주진 않는다. 어떤 컨테이너는 다른 컨테이너보다 몇 배 더 빠르다.

-vector를 반복실행하며 접근하는데엔 반복자사용/at()사용/첨자사용하는 방법이 있는데 첨자사용이 제일 빠르다.

-vector 삽입은 대입문/다른 컨테이너에서 반복자를 통해 insert 사용/push_back()사용/벡터 뒤에 insert()를 사용하는 방법으로 시도 

https://modoocode.com/186

 

C++ 레퍼런스 - vector::insert

 

modoocode.com