by 꽈배기
template
키워드와 C#의 Generic
키워드의 차이점 (컴파일, 링커, 런타임 기준)template
링커 타임: 템플릿 인스턴스화는 각 번역 단위(translation unit)에서 독립적으로 발생할 수 있습니다. 따라서 동일한 템플릿 인스턴스가 여러 번 생성될 수 있으며, 링커는 이러한 중복된 인스턴스를 제거하는 역할을 합니다.
런타임: 템플릿은 컴파일 타임에 모든 타입 정보가 결정되므로, 런타임 오버헤드는 없습니다. 생성된 코드는 일반 함수나 클래스와 동일하게 동작합니다.
Generic
컴파일 타임: C# 제네릭은 컴파일 타임에 타입 안전성을 보장합니다. 제네릭 타입을 사용하는 코드가 컴파일될 때, 컴파일러는 타입 검사를 수행하지만, 구체적인 타입으로 인스턴스화하지는 않습니다.
닷넷 컴파일: C#의 제네릭은 .NET 중간 언어(Immediate Language)로 컴파일되며, 이 IL 코드에는 제네릭 타입 정보가 포함됩니다. 이 IL 코드를 처리하여 어셈블리를 생성합니다.
런타임: C#의 제네릭은 런타임에 JIT(Just-In-Time) 컴파일러에 의해 구체적인 타입으로 인스턴스화됩니다. 즉, 제네릭 타입이 처음 사용될 때, JIT 컴파일러가 해당 타입에 맞는 코드를 생성합니다. 이로 인해 런타임 오버헤드가 발생할 수 있습니다.
템플릿은 컴파일 타임에 인스턴스화 되며 제너릭은 타입 검사를 수행한다.
템플릿은 중복 인스턴스된 개체를 링커가 제거한다.
제너릭은 런타임에 JIT 방식으로 인스턴스화 된다.
constexpr
가 내부적으로 어떤 동작을 하는지 설명constexpr
는 C++11에서 도입된 키워드로, 컴파일 타임 상수 표현식을 정의하는 데 사용됩니다. constexpr
함수나 변수는 컴파일 타임에 평가될 수 있는 표현식을 나타냅니다.
constexpr
로 선언된 함수나 변수는 컴파일 타임에 평가됩니다. 컴파일러는 이 표현식을 컴파일 타임에 계산하여 상수 값을 결정합니다. 이를 통해 런타임 오버헤드를 줄일 수 있습니다.constexpr
함수는 컴파일 타임에 인라인으로 확장될 수 있으며, 컴파일러는 이 함수를 호출하는 코드를 컴파일 타임에 평가합니다. 단, 모든 입력이 컴파일 타임 상수여야 합니다.constexpr
변수는 반드시 컴파일 타임 상수로 초기화되어야 합니다. 컴파일러는 이 변수를 상수로 취급하여 최적화할 수 있습니다.template
가 오버헤드를 일으키는 이유C++ 템플릿은 강력한 기능을 제공하지만, 몇 가지 오버헤드를 일으킬 수 있습니다:
코드 중복: 템플릿은 각 타입에 대해 별도의 인스턴스를 생성합니다. 예를 들어, std::vector
와 std::vector
는 서로 다른 두 개의 클래스 정의를 생성합니다. 이는 코드 크기를 증가시키고, 바이너리 파일의 크기를 늘릴 수 있습니다.
컴파일 시간 증가: 템플릿 인스턴스화는 컴파일 타임에 수행되므로, 복잡한 템플릿을 많이 사용할수록 컴파일 시간이 증가할 수 있습니다. 특히, 템플릿 메타프로그래밍을 사용할 경우 컴파일 시간이 크게 늘어날 수 있습니다.
디버깅 복잡성: 템플릿 코드는 디버깅이 어렵습니다. 컴파일러가 생성한 코드가 복잡하고, 에러 메시지가 이해하기 어려울 수 있습니다. 이는 개발자의 디버깅 시간을 증가시킬 수 있습니다.
링커 오버헤드: 동일한 템플릿 인스턴스가 여러 번 생성될 수 있으며, 링커는 이러한 중복된 인스턴스를 제거해야 합니다. 이는 링커의 작업량을 증가시킬 수 있습니다.
템플릿 (C++):
제너릭 (Java, C#):
JIT와 제너릭의 관계:
정리:
각 번역 단위에서 템플릿이 사용될 때마다 컴파일러는 해당 템플릿의 인스턴스를 생성합니다. 이는 각 번역 단위에서 독립적으로 발생합니다.
여러 번역 단위에서 동일한 템플릿 타입을 사용하면, 각 번역 단위에서 동일한 템플릿 인스턴스가 중복 생성됩니다.
링커는 여러 번역 단위에서 생성된 동일한 템플릿 인스턴스를 감지하고 중복을 제거합니다. 이 과정은 다음과 같습니다: a) 각 템플릿 인스턴스의 심볼(symbol)을 비교합니다. b) 동일한 심볼을 가진 인스턴스들을 찾아냅니다. c) 중복된 인스턴스 중 하나만 남기고 나머지는 제거합니다. d) 제거된 인스턴스를 참조하던 코드를 남은 인스턴스로 리다이렉트합니다.
이 과정에서 컴파일 시간이 증가하는 이유는 다음과 같습니다: a) 각 번역 단위에서 템플릿 인스턴스화에 시간이 소요됩니다. b) 링커가 모든 템플릿 인스턴스를 비교하고 중복을 찾는 데 시간이 필요합니다. c) 중복 제거 과정에서 심볼 테이블을 수정하고 참조를 업데이트하는 데 시간이 소요됩니다.