본문 바로가기
NC University/Advanced C++

6일차 - thin template

by 날쑤 2015. 11. 4.

Intro

  임의의 타입 객체들을 저장하는 단순한 컨테이너를 구현해보자. 이 컨테이너의 기능은 컨테이너의 맨 앞에 객체를 추가하거나, 컨테이너에 저장된 첫 element를 얻어오는 것으로 충분하다. 또한, 컨테이너의 현재 상태(state)를 보여주는 함수 2개(size, empty)도 함께 포함시키자. 이러한 요구사항을 만족시키는 컨테이너의 선언은 아래의 코드와 같은 형태가 될 것이다.

  이제 위의 Container 템플릿을 아래와 같이 3개의 타입에 대해 인스턴스화 시키고, 각 객체의 모든 함수들을 한번 이상 호출했다고 가정해보자.

  이 경우 컴파일러에 의해 생성되는 함수는 3 * 4 = 12개가 된다. 즉, 타입 인자의 종류에 비례해서 object code에 의한 메모리 사용량이 증가한다. 그런데 size나 empty 함수는 타입 인자와 무관한 함수임에도 불구하고 각 템플릿 클래스마다 중복해서 포함되는데 이는 명백한 redundancy이다. 이러한 redundancy를 제거하면 단순히 메모리 사용량을 줄이는 것 뿐만 아니라, 같은 object code를 재사용하게 만듬으로써 instruction cache의 성능도 향상시킬 수 있다. 당연히 이는 어플리케이션의 성능도 따라서 향상됨을 의미한다. 이러한 아이디어에서 출발해서 등장한 기법이 바로 이번 글에서 소개할 thin template이다.

  우선 시작은 위에서 언급한 것처럼 템플릿 타입 파라메터에 독립적인 함수(변수)를 추출해내는 것으로 시작한다. 방법은 아래와 같이 기존 템플릿 내에서 해당되는 부분을 기반 클래스로 뽑아내면 된다.

  이제는 위와 동일한 상황에서 생성되는 함수의 개수는 2(기반 클래스의 함수 개수) + 2 * 3 = 8개가 된다. 즉, object code 단에서 중복되는 부분을 기반 클래스로 묶어냄으로써 생성되는 object code의 크기를 줄인 것이다. 이번에는 멤버 함수뿐만 아니라 포인터 변수까지 묶어내서 템플릿을 좀 더 슬림하게 만들어보자.

Thin template

  위의 코드에서 볼 수 있듯이 앞의 두 멤버함수와는 달리 포인터 변수인 buffer_는 템플릿 타입 파라메터 T에 종속적이다.하지만 C++에는(정확히는 C에는) 타입에 상관없이 모든 주소값을 저장할 수 있는 방법이 이미 존재한다. 바로 void형 포인터가 그것이다. 우선 void형 포인터를 이용해서 기반 클래스인 ContainerImpl을 재구성해보자.

  사실 따로 템플릿을 만들지 않고 ContainerImpl을 바로 사용해도 원래의 요구사항은 충족시킬 수 있다. 하지만 이 경우 push_front와 front 함수를 사용할 때마다 캐스팅을 직접해야 하는 불편함이 따른다. 그래서 앞의 코드와 같이 Container 템플릿이 ContainerImpl을 상속받게 하고, Container 템플릿 내부에서는 타입 인자를 활용해서 캐스팅만을 책임지게 만든다. 이를 통해 캐스팅은 내부적으로 감추면서(encapsulated) 타입 안정성을 보장받을 수 있다.

  한편, 템플릿 내의 각 함수는 기반 클래스에 존재하는 함수를 그냥 호출하거나 혹은 캐스팅을 더하는 수준이다. 그러므로 이들을 inline으로 선언하면 함수 호출 오버헤드와 추가적인 object code의 생성을 막을 수 있다. 위의 내용들을 모두 반영해서 최종적으로 얻게되는 Container 템플릿은 다음과 같다.

  정리하면 thin template 기법은 다음과 같은 작업들을 한다.

  1. 템플릿 타입 파라메터 T와 관계없는 멤버함수(혹은 변수)들은 모두 기반 클래스로 추출
  2. T* 은 기반 클래스에서 void형 포인터로 처리하고, 이를 상속받은 템플릿에서는 캐스팅과 기반 클래스의 함수 호출을 수행
  3. 템플릿 내의 각 함수들은 모두 inline으로 선언

  그리고 이는 클래스 템플릿이 여러 타입에 대해 인스턴스화될 때 중복된 object code에 의한 불필요한 메모리 사용을 제거하고, object code를 재사용함으로써 어플리케이션의 성능을 향상시키는 데 도움을 준다.