일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | |||
5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 | 27 | 28 | 29 | 30 | 31 |
Tags
- c++
- 언리얼
- effective stl
- resource management class
- 다형성
- 반복자
- 오블완
- 메타테이블
- 게임
- 참조자
- virtual function
- 티스토리챌린지
- 영화
- reference
- 스마트 포인터
- 함수 객체
- Smart Pointer
- operator new
- UE4
- exception
- 루아
- more effective c++
- Effective c++
- lua
- 암시적 변환
- 예외
- implicit conversion
- 영화 리뷰
- 비교 함수 객체
- 상속
Archives
- Today
- Total
스토리텔링 개발자
[More Effective C++] 18. 과도 선행 평가 본문
728x90
항목 18. 예상되는 계산 결과를 미리 준비하면 처리 비용을 깎을 수 있다.
과도 선행 평가(over-eager evaluation)
- 자주 요구될 것 같은 계산이 있다면, 그 요구를 효율적으로 처리할 수 있는 자료구조를 설계하여 비용을 낮추자.
template<typename NumericalType>
class DataCollection
{
public:
// 아래 수치 데이터들은 과도 선행 평가로 성능 향상을 꾀할 수 있다.
NumericalType min() const;
NumericalType max() const;
NumericalType avg() const;
...
};
캐싱(caching)
- 가장 간단하게 구현할 수 있는 방법
- 계산이 끝났고, 또 다시 사용될 것 같은 값을 캐싱한다.
int findCubicleNumber(const string& employeeName)
{
typedef map<string, int> CubicleMap;
static CubicleMap cubes; // 캐싱 데이터 컨테이너
CubicleMap::iterator it = cubes.find(employeeName);
if(it == cubes.end())
{
// 값이 없으면 캐싱한 후 리턴한다.
int cubicle = cubes[employeeName] = cubicle;
return cubicle;
}
else
{
// 값이 있으면 캐싱된 값을 리턴한다.
return (*it).second;
}
}
미리 가져오기(Prefetching)
- 사용자가 요구하는 양 이상의 양을 미리 할당해둔다.
- 라면을 여러 박스 샀을 때 라면 값을 할인받는 것과 비슷하다.
- 디스크 컨트롤러는 디스크에서 데이터를 읽을 때,
- 프로그램 쪽에서 적은 양의 데이터만 요구했더라도, 블록 하나 혹은 섹터 하나를 통째로 읽는다.
- 조금씩 여러 번 읽는 것보다 한번 많이 읽는 쪽이 빠르기 때문이다.
- 또한, 인접한 데이터들이 연달아 사용되는 경우가 많기 때문이다.
- 참조 위치의 근접성(locality of reference)
templcate<typename T>
class DynArray { ... }; // 자체 확장할 수 있는 배열
DynArray<double> a;
a[22] = 3.5; // a의 유효범위는 0 ~ 22
a[32] = 0; // a의 유효범위는 0 ~ 32
// 추가 메모리 할당의 기본적인 로직
template<typename T>
T& DynArray<T>::operator[](int index)
{
if(index < 0)
throw 예외; // 음수는 불가
if(index > 현재의 최대 인덱스)
{
new를 사용하여 현재 인덱스를 index까지 추가 메모리 할당
}
return 배열 내 index 번째 요소
}
// 과도 선행 평가를 반영한 로직
template<typename T>
T& DynArray<T>::operator[](int index)
{
if(index < 0)
throw 예외; // 음수는 불가
if(index > 현재의 최대 인덱스)
{
int diff = index - 현재의 최대 인덱스;
현재의 최대 인덱스 = index + diff 가 되도록 메모리 할당
// 즉, 필요한 메모리 양의 두배 만큼을 한당한다.
}
return 배열 내 index 번째 요소
}
DynArray<double> a;
// 과도 선행 평가를 사용한 경우
a[22] = 3.5; // 인덱스 44가 유효하도록 메모리가 할당된다.
a[32] = 0; // 인덱스 44 이내이기 때문에 메모리 할당이 없다.
정리
- 메모리를 많이 쓰면 속도가 빨라진다.
- 공간과 시간은 함께 절약하기 힘들다.
- 물론 항상 그런 것은 아니다.
- 메모리 점유량이 큰 객체는 가상 메모리나 캐시 페이지에 들어가지 못한다.
- 아주 드문 경우지만, 객체의 크기를 키워도 소프트웨어의 효율이 떨어질 수도 있다.
- 운영체제의 메모리 관리 시스템이 페이징을 하는 횟수가 늘어나면 캐시 적중률(hit ratio)가 떨어지기 때문이다.
- 그러므로 프로파일링은 필수! (항목 16 참조)
- 과도 선행 평가가 지연 평가와 상충되는 것은 아니다.
- 지연 평가는 자주 사용되는 게 아니면 최대한 늦게 하기.
- 과도 선행 평가는 자주 사용된다면 미리 많이 하기.
728x90
'개발 > More Effective C++' 카테고리의 다른 글
[More Effective C++] 20. 반환값 최적화(return value optimization) (0) | 2024.08.27 |
---|---|
[More Effective C++] 19. 임시 객체(temporaries) (0) | 2024.08.26 |
[More Effective C++] 17. 지연 평가 (0) | 2024.08.22 |
[More Effective C++] 16. 파레토 법칙 (0) | 2024.08.21 |
[More Effective C++] 15. 예외 처리 비용 (0) | 2024.08.20 |
Comments