LLVM 기반 언어의 C++ 대비 성능

LLVM 기반 언어의 C++ 대비 성능

2025-10-27, G25DR

1. 요약

본 보고서는 “LLVM으로 컴파일되고 최적화되는 프레임워크는 C++에 준하는 성능을 내는가?“라는 질문에 대해 심층적인 기술 분석을 제공한다. 결론적으로, LLVM은 고성능을 위한 강력한 기반을 제공하지만, C++ 수준의 속도를 달성하는 것은 자동적으로 보장되지 않는다. 이는 해당 언어의 설계 철학, 메모리 관리 모델, 그리고 프론트엔드가 LLVM 최적화기에 얼마나 유용한 정보를 제공하는지에 따라 달라진다. Rust와 같은 언어는 성능 동등성 달성이 가능하며 때로는 C++를 능가함을 보여준다. 반면, Swift와 같은 언어는 자동 메모리 관리와 같은 개발자 중심 기능을 위해 순수 성능과 의도적인 트레이드오프를 만든다. 본 분석은 아키텍처 심층 분석과 실증적인 벤치마크 데이터를 통해 이러한 미묘한 차이를 해부한다.

2. 고성능의 기반으로서의 LLVM 컴파일러 인프라스트럭처

이 섹션에서는 LLVM 아키텍처에 대한 기초 지식을 확립하고, 왜 이것이 현대 고성능 언어 구현의 사실상 표준이 되었는지 설명한다. C++와 Clang을 구체적인 예로 사용하여 소스 코드에서 최적화된 기계 코드로 변환되는 컴파일 과정을 명확히 추적한다.

2.1 모듈식 “기술의 집합체”: LLVM 프로젝트 해부

LLVM은 단일 컴파일러가 아니라, 고도로 최적화된 컴파일러, 옵티마이저, 런타임 환경 구축을 위한 모듈식 및 재사용 가능한 컴파일러 및 툴체인 기술의 집합체로 정의된다.1 LLVM 프로젝트는 C++로 작성되었으며, 컴파일 타임, 링크 타임, 런타임 최적화를 위해 설계되었다.2 이 모듈식 구조는 LLVM의 가장 큰 강점이며, 세 가지 주요 단계로 구성된다.

  • 프론트엔드 (Frontend): 이 단계는 특정 프로그래밍 언어(예: C++, Rust, Swift)의 소스 코드를 파싱하여 추상 구문 트리(AST, Abstract Syntax Tree)로 변환하고, 이를 다시 LLVM의 공통 중간 표현(IR, Intermediate Representation)으로 변환하는 역할을 한다.3 LLVM 프로젝트의 공식 C, C++, Objective-C 프론트엔드로는 Clang이 있다.1
  • 미들엔드 (Middle-End / Optimizer): 이 단계는 LLVM의 최적화 능력의 핵심이다. 프론트엔드에서 생성된 LLVM IR을 입력으로 받아, 원본 소스 언어나 최종 타겟 아키텍처에 대한 지식 없이 코드의 효율성을 개선하기 위해 “패스(Passes)“라고 불리는 일련의 변환 작업을 적용한다.2
  • 백엔드 (Backend / Code Generator): 이 마지막 단계에서는 최적화된 LLVM IR을 타겟별 기계 코드(예: x86_64, ARM)로 변환한다.3 LLVM은 단일 툴체인으로 여러 아키텍처를 타겟으로 할 수 있는 네이티브 크로스 컴파일러로 설계되어, 플랫폼 간 이식성을 크게 향상시킨다.9

2.2 컴파일의 공용어: LLVM IR의 핵심적 역할

LLVM의 강력함은 언어 독립적인 중간 표현(IR)에서 나온다. LLVM IR은 이식성이 높은 고수준 어셈블리 언어 역할을 하며, LLVM 생태계의 중심축을 담당한다.2

  • LLVM IR의 정의: LLVM IR은 강력한 타입 시스템을 갖추고 있으며, 모든 변수가 한 번만 할당되는 정적 단일 할당(SSA, Static Single Assignment) 형태를 사용한다. 이 SSA 형태는 변수 간의 의존성 분석을 단순화하여 매우 강력하고 효율적인 최적화를 가능하게 한다.2
  • IR의 중요성: IR은 언어 프론트엔드와 최적화기/백엔드 사이의 보편적인 인터페이스 역할을 한다. 이는 LLVM IR로 컴파일될 수 있는 모든 언어(Ada, C++, D, Fortran, Haskell, Julia, Rust, Swift 등)가 동일한 강력한 최적화 인프라를 활용할 수 있음을 의미한다.2 이 점이 바로 사용자의 질문에 대한 핵심적인 답변의 시작점이다. 즉, 서로 다른 언어들이 어떻게 유사한 최고 수준의 성능을 목표로 할 수 있는지에 대한 공통 메커니즘을 제공하는 것이다.
  • 공유 백엔드의 힘: 이러한 설계 덕분에 컴파일러 개발자들은 언어의 고유 기능과 프론트엔드 설계에 집중하면서도, 다양한 아키텍처에 대한 최적화 및 코드 생성 분야에서 수십 년간 축적된 집단적인 연구 성과를 활용할 수 있다.2

이러한 LLVM의 모듈식 아키텍처는 언어 간 성능 비교의 패러다임을 바꿨다. 과거에는 C++(GCC 사용)와 Haskell(GHC 사용)의 성능을 비교하는 것은 완전히 다른 컴파일러 기술 스택을 비교하는 것과 같아서, 언어 자체의 의미론적 차이와 컴파일러 기술의 차이를 분리하기 어려웠다. 그러나 LLVM은 언어별 프론트엔드를 언어에 구애받지 않는 미들엔드(최적화기) 및 백엔드와 분리한다.1 이로 인해 Rust, Swift, C++(Clang 사용)와 같은 언어들은 모두 LLVM IR 단계에서 하나로 수렴하며, 정확히 동일한 최적화 엔진과 코드 생성기를 공유하게 된다. 따라서 LLVM은 성능 방정식에서 통제 변수 역할을 하여, 언어 설계와 프론트엔드 구현이 성능에 미치는 영향을 분리하여 분석할 수 있게 해준다. “X 언어가 C++ 성능에 필적할 수 있는가?“라는 질문은 “X 언어의 프론트엔드가 C++용 Clang이 생성하는 IR만큼 최적화 가능한 IR을 생성할 수 있는가?“라는 더 구체적이고 의미 있는 질문으로 전환된다.

2.3 사례 연구: C++ 프로그램의 Clang/LLVM 파이프라인 추적

간단한 C++ 프로그램이 최종 실행 파일로 변환되는 과정은 LLVM의 작동 방식을 명확히 보여준다.

  1. 프론트엔드 (Clang): 간단한 C++ 함수가 소스 코드로 제공된다. Clang은 어휘 분석 및 의미 분석을 수행하여 AST를 생성한다.4
  2. IR 생성: Clang의 Sema 모듈은 AST를 최적화되지 않은 LLVM IR로 변환한다.8 이 IR은 프로그램의 논리를 언어 독립적인 형태로 표현한다. 개발자는 clang++ -S -emit-llvm 명령어를 사용하여 이 중간 결과를 직접 확인할 수 있다.3
  3. 미들엔드 (Optimizer): opt 도구(또는 내부적으로 동등한 파이프라인)가 IR에 대해 일련의 최적화 패스를 실행한다.11 이 단계에서 비효율적인 IR이 고도로 최적화된 버전으로 변환된다.
  4. 백엔드 (Code Generation): llc 도구(또는 동등한 컴포넌트)가 최적화된 IR을 받아 타겟 아키텍처에 맞는 어셈블리 코드를 생성한다.11
  5. 링킹 (Linking): 마지막으로, LLD(LLVM의 통합 링커)와 같은 링커가 목적 파일들을 결합하여 최종 실행 파일을 생성한다.6

3. 최적화의 메커니즘: LLVM이 코드를 기계 수준 효율성으로 정제하는 방법

이 섹션에서는 LLVM 성능의 “어떻게“에 대해 심층적으로 다룬다. 최적화라는 추상적인 개념에서 벗어나, LLVM 패스의 구체적인 예를 통해 코드를 변환하는 효과를 보여준다.

3.1 패스 프레임워크: 변환과 분석을 위한 구조적 접근법

LLVM의 최적화는 ’패스(Pass)’라는 모듈화된 단위들을 통해 체계적으로 이루어진다.

  • 패스의 정의: LLVM 패스는 프로그램을 순회하며 정보를 수집(분석 패스, Analysis Pass)하거나 프로그램을 변환(변환 패스, Transform Pass)하는 클래스이다.12 이러한 모듈식 구조 덕분에 작은 재사용 가능한 컴포넌트들로부터 복잡한 최적화 파이프라인을 구축할 수 있다.
  • 패스 관리자 (Pass Managers): FunctionPassManager, ModulePassManager와 같은 패스 관리자는 패스를 효율적으로 스케줄링하고, 분석 결과를 공유하여 재계산을 피하며, 함수 단위로 패스를 파이프라이닝하여 캐시 동작을 개선하는 역할을 한다.4
  • 패스 순서의 중요성: 최적화 패스의 실행 순서는 매우 중요하다. -O1, -O2, -O3와 같은 표준 최적화 레벨에 포함된 파이프라인은 각 패스가 서로의 작업을 기반으로 시너지를 내도록 신중하게 설계되었다.14 예를 들어, 상수 전파(Constant Propagation)를 실행한 후에 죽은 코드 제거(Dead Code Elimination)를 실행하는 것이 훨씬 효과적이다.

3.2 핵심 미들엔드 최적화 패스 분류

여기서는 몇 가지 대표적인 최적화 패스를 C++ 예제와 함께 설명하고, 이들이 LLVM IR에 미치는 영향을 기술한다.

  • 스칼라 최적화 (Scalar Optimizations):
  • 메모리-레지스터 승격 (mem2reg): 스택 변수(alloca)를 SSA 레지스터로 옮겨, 다른 많은 최적화가 가능하도록 하는 기반 패스이다.
  • 상수 전파 및 폴딩 (sccp): 컴파일 타임에 알려진 상수 값으로 변수를 대체하고 상수 표현식을 미리 계산한다.4
  • 죽은 코드 제거 (dce): 결과가 사용되지 않거나 도달할 수 없는 명령어 및 코드 블록을 제거한다.3
  • 제어 흐름 그래프 (CFG) 최적화:
  • CFG 단순화 (simplifycfg): 기본 블록을 병합하고, 빈 블록을 제거하며, 제어 흐름을 단순화하여 다른 패스가 프로그램을 더 쉽게 분석할 수 있도록 만든다.12
  • 루프 최적화 (Loop Optimizations):
  • 루프 불변 코드 이동 (licm): 루프 내에서 변하지 않는 계산을 루프 밖으로 끌어내어 반복적인 연산을 방지한다.4
  • 루프 펼치기 (loop-unroll): 루프 본문을 복제하여 분기 오버헤드를 줄이고, 스케줄링 및 벡터화를 위해 더 많은 명령어를 노출시킨다.4
  • 프로시저 간 최적화 (Interprocedural Optimizations):
  • 함수 인라이닝 (Function Inlining): 함수 호출을 호출된 함수의 본문으로 대체하여 호출 오버헤드를 제거하고, 함수 간 최적화를 가능하게 한다.17

이러한 최적화 과정은 C++ 프로그래머가 생각하는 “사용되지 않는 변수 제거“나 “루프 밖으로 계산 이동“과 같은 고수준의 의미론적 개념과는 다른 차원에서 작동한다. LLVM은 C++의 “변수“나 “루프“를 이해하는 것이 아니라, SSA 레지스터, 기본 블록, phi 노드, br 명령어와 같은 저수준 IR 구조를 이해한다.3 dce 패스는 정의되었지만 사용되지 않는 SSA 레지스터를 식별하고, licm 패스는 CFG의 역방향 엣지(back-edge)로 정의된 루프 구조 내에서 루프 내부에서 정의된 값에 의존하지 않는 명령어를 식별하여 작동한다. Clang이 C++를 LLVM IR로 변환할 때, C++의 “변수“는 SSA 레지스터가 되고 “루프“는 특정 점프 패턴을 가진 기본 블록 집합이 된다. 따라서 고수준의 최적화는 단일의 거대한 “C++ 코드 최적화” 단계가 아니라, 저수준 표현에 대해 간단하고 일반적이며 강력한 변환 규칙들을 순차적으로 적용한 결과로 나타나는 창발적 속성(emergent property)이다. 이것이 바로 LLVM이 수많은 다른 언어들을 효과적으로 최적화할 수 있는 핵심적인 이유이다.

3.3 최적화 시각화: LLVM IR의 “전“과 “후”

최적화의 실제 효과를 확인하는 가장 좋은 방법은 Compiler Explorer(godbolt.org)와 같은 도구를 사용하는 것이다.3

  • 예제 시나리오: 사용되지 않는 변수와 명백한 불변 코드를 포함하는 루프가 있는 간단한 C++ 함수를 가정해보자.

C++

int test() {
int a = {0};
int b = {0};
for (int i = 0; i < 15; i++){
a[b[i]]++;
}
return 0;
}
  • clang++ -O0 -S -emit-llvm: 최적화가 꺼진 상태에서 생성된 IR은 소스 코드의 논리를 거의 그대로 반영하는 장황한 형태를 띤다. 루프의 각 단계, 배열 접근, 증가 연산이 명시적으로 나타난다.18

  • clang++ -O2 -S -emit-llvm: 최적화가 적용되면 IR은 극적으로 단순해진다. Clang은 a 배열이 함수 외부에서 절대 사용될 수 없다는 것을 감지한다. 이는 a 배열에 대한 모든 연산이 프로그램의 다른 부분에 영향을 미치지 않으며, 따라서 제거해도 무방하다는 것을 의미한다. 이 연산을 제거하면 부수 효과(side effect)가 없는 빈 for 루프만 남게 되며, 이 루프 또한 제거될 수 있다. 결국 함수는 사실상 빈 함수가 되어 ret i32 0이라는 단일 명령어로 축소된다.18

이 시각적 비교는 2.2절에서 설명한 추상적인 패스들(dce, licm 등)이 어떻게 협력하여 코드를 근본적으로 변환하는지를 구체적으로 보여준다.

4. 비교 성능 분석: C++ 대 현대 LLVM 기반 언어

이 섹션에서는 사용자의 질문에 직접적으로 답하기 위해 세 가지 주요 LLVM 기반 언어인 Rust, Swift, Julia의 성능을 C++ 벤치마크와 비교하여 검토한다. 각 하위 섹션에서는 벤치마크 데이터를 분석하고 성능 결과를 해당 언어의 특정 설계 철학 및 기능과 연결한다.

4.1 Rust: 제로-코스트 추상화와 메모리 안전성을 통한 성능 동등성

Rust는 C++와 필적하는 성능을 목표로 설계되었으며, 많은 경우 이 목표를 달성하거나 능가한다. 이는 “제로-코스트 추상화“와 컴파일 타임에 보장되는 강력한 메모리 안전성 덕분이다.

4.1.1 벤치마크 데이터 분석: C++ (Clang) 대 Rust

The Computer Language Benchmarks Game과 같은 소스에서 제공하는 벤치마크 결과는 매우 경쟁적인 양상을 보여준다. 일부 테스트에서는 Rust가 더 빠르고, 다른 테스트에서는 C++가 더 빠르며, 종종 통계적으로 구별할 수 없을 정도로 유사한 성능을 보인다.20 이는 성능이 언어 자체뿐만 아니라 특정 구현 및 알고리즘에 크게 의존한다는 것을 시사한다.20

벤치마크가장 빠른 언어 (구현)C++ (Clang) 시간(초)Rust 시간(초)비고
n-bodyRust (#9)2.202.19두 구현 모두 고도로 최적화됨
fannkuch-reduxC++ (Clang #6)2.253.81C++ 구현이 SIMD 명령어를 효과적으로 사용
mandelbrotRust (#4)1.230.95Rust 구현이 더 빠른 성능을 보임
spectral-normC++ (Clang #6)0.390.72C++ 구현이 약 2배 빠름

표 1: C++ (Clang) 대 Rust 성능 벤치마크 요약 21

4.1.2 “제로-코스트 추상화” 해부

Rust의 핵심 철학은 고수준의 프로그래밍 구조(이터레이터, 클로저, async/await 등)를 사용하더라도, 개발자가 저수준 코드를 직접 작성한 것과 동일한 효율성의 기계 코드로 컴파일되어야 한다는 것이다.22 “사용하지 않는 것에 대해서는 비용을 지불하지 않고, 사용하는 것에 대해서는 직접 코딩하는 것보다 더 나을 수 없다“는 모토가 이를 잘 나타낸다.24

  • 메커니즘 1: 단형성 (Monomorphization): Rust는 제네릭 함수나 구조체를 사용할 때, 컴파일 타임에 사용된 각 구체적인 타입에 대한 특수화된 버전을 생성한다. 이는 다른 언어의 제네릭에서 흔히 발생하는 동적 디스패치나 런타임 타입 검사의 오버헤드를 완전히 제거한다.22
  • 메커니즘 2: 인라이닝 (Inlining): 컴파일러는 작은 함수와 클로저, 특히 .filter().map()과 같은 이터레이터 체인 내의 클로저들을 공격적으로 인라이닝한다. 이 과정에서 여러 단계의 추상화가 중간 할당이나 함수 호출 오버헤드 없이 단일의 최적화된 루프로 융합된다.22

4.1.3 소유권 모델의 성능적 함의

Rust의 소유권(Ownership) 및 대여 검사기(Borrow Checker)는 단순히 메모리 안전성을 위한 기능이 아니다. 이는 컴파일러에게 강력한 최적화 단서를 제공하는 성능 향상 기능이기도 하다.

  • GC 없는 메모리 안전성: Rust의 소유권 시스템은 컴파일 타임에 메모리 안전성을 보장하여, 런타임 가비지 컬렉터와 그로 인한 예측 불가능한 중단(pause)을 제거한다.22
  • 에일리어싱(Aliasing) 이점: 이것이 핵심적인 성능 통찰이다. 대여 검사기는 가변 참조(&mut T)가 해당 스코프 내에서 유일하다는 것을 보장한다. 이는 컴파일러에게 C의 restrict 키워드나 C++의 __restrict 확장 기능과 동일한 정보를, 하지만 기본적으로 그리고 정확성이 보장된 상태로 제공한다. 이 “noalias” 정보는 포인터 에일리어싱이 가능할 경우 안전하지 않은 명령어 재정렬 및 벡터화와 같은 더 공격적인 최적화를 LLVM 백엔드가 수행할 수 있도록 허용한다.28

컴파일러 최적화의 주요 난제 중 하나는 포인터 에일리어싱이다. 즉, 두 개의 다른 포인터(*p*q)가 동일한 메모리 위치를 가리킬 수 있는가? 만약 그럴 가능성이 있다면, 최적화기는 보수적으로 접근해야 한다. p를 통한 쓰기와 q로부터의 읽기 순서를 바꿀 수 없는데, 이는 프로그램의 동작을 변경할 수 있기 때문이다. C++에서는 컴파일러가 일반적으로 포인터가 에일리어싱될 수 있다고 가정해야 하므로 많은 최적화가 제한된다. 개발자는 __restrict를 사용하여 포인터가 에일리어싱되지 않음을 컴파일러에 약속할 수 있지만, 이는 수동적이고 오류가 발생하기 쉬우며, C++23 이전까지는 표준의 일부가 아니었다. 반면, Rust의 대여 검사기는 컴파일 타임에 가변 참조가 유일하다는 것을 증명한다.28 이것은 힌트가 아니라 검증 가능한 사실이다. Rust 프론트엔드(rustc)는 LLVM IR의 해당 포인터에 noalias 속성을 추가할 수 있다. 이 noalias 속성은 다른 방법으로는 비활성화되었을 수많은 LLVM 최적화를 활성화한다. LLVM은 이제 다른 포인터가 기본 메모리를 수정할 수 없다는 것을 알기 때문에 메모리 연산을 자유롭게 재정렬하고, 루프를 벡터화하며, 값을 레지스터에 더 오래 유지할 수 있다. 따라서 일부 경우 Rust의 우수한 성능은 언어 설계가 공유 LLVM 백엔드를 더 효과적으로 작동하도록 지원한 직접적인 결과이다. 개발자에게 부과되는 대여 검사기의 “비용“은 생성된 코드의 품질로 “보상“받는 셈이다.

4.2 Swift: 성능, 안전성, 그리고 추상화 오버헤드의 프로파일

Swift는 Apple 생태계 내에서 생산성과 안전성을 목표로 설계된 현대적인 언어이다. LLVM을 기반으로 하여 높은 성능을 내지만, C++와의 비교에서는 특정 상황에서 성능 저하를 보이는 트레이드오프가 존재한다.

4.2.1 벤치마크 데이터 분석: C++ (g++) 대 Swift

벤치마크 데이터에 따르면, Swift는 컴파일 언어로서 성능이 우수하지만, 특히 계산 집약적이고 메모리 바운드된 작업에서는 종종 C++에 뒤처진다.29 특히 GEMM(메모리 바운드, 순차 접근) 및 FFT(메모리 바운드, 불규칙 접근)와 같은 워크로드에서는 2014년의 한 연구에서 성능 격차가 6배에서 24배에 달하는 것으로 나타났다.30 이후 성능이 개선되었지만, 근본적인 원칙은 여전히 유효하다.

벤치마크가장 빠른 언어 (구현)C++ (g++) 시간(초)Swift 시간(초)성능 비율 (C++/Swift)
fannkuch-reduxC++ (#6)3.338.20~2.46x
n-bodyC++ (#4)5.465.48~1.00x
mandelbrotC++ (#4)0.891.35~1.52x
reverse-complementC++ (#2)0.762.05~2.70x

표 2: C++ (g++) 대 Swift 성능 벤치마크 요약 29

4.2.2 자동 참조 카운팅(ARC)의 성능 비용

Swift의 성능 특성을 이해하는 데 가장 중요한 요소는 자동 참조 카운팅(ARC)이다.

  • 메커니즘: Swift 컴파일러는 힙에 할당된 객체(클래스)의 생명주기를 관리하기 위해 retainrelease 호출을 자동으로 삽입한다.27 이는 수동 new/delete나 완전한 가비지 컬렉터 없이 메모리 안전성과 편의성을 제공한다.
  • 오버헤드:retain/release 호출은 런타임 연산이다. 빡빡한 루프나 복잡한 객체 그래프에서는 참조 카운트를 원자적으로 증가시키고 감소시키는 오버헤드가 상당한 성능 병목 현상을 유발할 수 있다.31 이는 수동으로 관리되는 C++ 코드(스택 객체나 unique_ptr를 사용하는 RAII)에서는 발생하지 않는 비용이다.

4.2.3 기타 성능 요인 조사

  • 값 타입 대 참조 타입: Swift의 struct(값 타입, 스택 할당)와 class(참조 타입, ARC를 사용하는 힙 할당) 간의 성능 차이를 강조하는 것이 중요하다. 고성능 Swift 코드는 ARC 오버헤드를 피하기 위해 struct를 적극적으로 사용한다.31
  • 컴파일러 벡터화 문제: 초기 Swift 컴파일러는 Swift의 고수준 배열 추상화를 포함하는 루프를 벡터화하는 데 어려움을 겪었다는 분석이 있다.30 이는 C++ 컴파일러가 이미 해결한 문제였다. 비록 이 문제는 개선되었지만, 프론트엔드의 추상화가 백엔드의 최적화 기회를 어떻게 가릴 수 있는지를 보여주는 좋은 예이다.
  • 동적 디스패치: 프로토콜의 메서드 호출은 C++의 가상 호출보다 느릴 수 있는 추가적인 간접 참조(프로토콜 증인 테이블)를 포함할 수 있다. 이는 컴파일러가 인라이닝이나 제네릭을 통해 가상화를 해제하지 않는 한 성능 저하 요인이 된다.31

Swift는 Rust나 C++/Clang과 마찬가지로 LLVM을 백엔드로 사용하므로 이론적으로는 동일한 최적화 능력을 가져야 한다.31 그러나 벤치마크 데이터는 특정 영역에서 지속적인 성능 격차를 보여준다.29 이러한 성능 격차의 원인을 추적하면 주로 클래스 타입에 대한 ARC와 같은 Swift 고유의 언어 기능으로 귀결된다.30 Swift 프론트엔드는 언어의 의미론에 따라 LLVM IR에 참조 카운팅 명령어를 삽입해야 한다. 이 명령어들은 런타임에 수행되어야 하는 실제 작업이다. LLVM이 이를 어느 정도 최적화할 수 있지만(예: 중복된 retain/release 쌍 제거), 컴파일 타임에 생명주기를 알 수 없는 객체에 대한 참조 카운팅의 근본적인 필요성을 제거할 수는 없다. 이는 “의미론적 오버헤드“를 생성한다. 즉, 언어 자체가 아무리 강력한 백엔드라도 완전히 제거할 수 없는 성능 비용을 도입하는 것이다. 이는 Rust의 “제로-코스트” 철학과 뚜렷한 대조를 이룬다. 이는 모든 경우에 절대적인 최대 성능을 달성하는 것보다 메모리 관리의 용이성을 우선시하는 Apple의 의도적인 설계 선택이다.32

4.3 Julia: 특화된 JIT 컴파일을 통한 고성능 수치 계산

Julia는 동적 언어의 사용 편의성과 컴파일 언어의 성능을 결합하여 과학 및 수치 계산 분야에서 C++와 경쟁한다. 이는 LLVM을 활용하는 독특한 JIT(Just-In-Time) 컴파일 방식 덕분이다.

4.3.1 벤치마크 데이터 분석: C++ 대 Julia

Julia는 목표 분야인 수치 및 과학 컴퓨팅에서 C++와 매우 경쟁력이 있으며, 때로는 필적하는 성능을 보인다.36

4.3.2 “런타임 AOT” 모델: 차별화된 JIT 컴파일

  • AOT와의 대조: C++는 사전(AOT, Ahead-Of-Time) 컴파일된다. 컴파일러는 소스 코드에서 사용할 수 있는 정적 정보만을 기반으로 최적화한다.39
  • Julia의 JIT: Julia는 JIT(Just-In-Time) 컴파일러를 사용한다. 함수의 컴파일은 해당 함수가 처음 실행될 때 발생한다.39
  • 런타임 타입 활용: 핵심적인 장점은 Julia의 JIT 컴파일러가 컴파일 시점에 함수 인자의 구체적인 타입을 안다는 것이다. 이를 통해 해당 특정 타입에 고도로 특화되고 최적화된 버전의 함수를 생성할 수 있다. 이는 동적 디스패치를 제거하고 공격적인 인라이닝 및 최적화를 가능하게 하여, AOT 세계의 제네릭 함수로는 불가능한 수준의 최적화를 달성한다.41 이것이 바로 Julia의 JIT가 “런타임에 실행되는 AOT 컴파일러“라고 불리는 이유이다.41

4.3.3 성능 트레이드오프: 첫 호출 지연 시간

  • “JIT 세금”: 이 접근 방식의 주된 단점은 초기 컴파일 오버헤드이다. 함수가 새로운 타입 조합의 인자로 처음 호출될 때, JIT 컴파일러가 실행되면서 눈에 띄는 지연이 발생한다.41
  • 상각 (Amortization): 장시간 실행되는 수치 시뮬레이션의 경우, 이 초기 비용은 이후 수많은 고도로 최적화된 실행을 통해 상각된다. 그러나 낮은 시작 지연 시간이 요구되는 애플리케이션에서는 이것이 상당한 단점이 될 수 있다.39

전통적인 성능 계층 구조는 AOT 컴파일 언어(C++)를 JIT 컴파일 언어(Java, Python)보다 상위에 둔다. Julia는 C++와 동일한 고품질 백엔드(LLVM)를 JIT 컨텍스트에서 사용함으로써 이 구도를 흔든다.41 C++ 템플릿 함수 template<typename T> T process(T x)를 생각해보자. AOT 컴파일러는 프로그램에서 사용된 모든 인스턴스화에 대한 코드를 생성해야 하며, 보지 못한 타입에 대한 코드는 생성할 수 없다. 반면 Julia 함수 function process(x)Float64로 호출될 때 JIT 컴파일러가 Float64에만 특화된 process 버전을 생성한다. 이는 다른 타입에서는 유효하지 않을 수 있는 Float64의 속성을 기반으로 최적화를 수행할 수 있게 한다. 이는 Julia가 C++와 같은 정적 타입 제약 없이 C++ 수준의 성능을 달성할 수 있음을 의미한다. 컴파일러가 사실상 런타임에 템플릿 특수화를 수행하는 것이다. 이는 근본적인 트레이드오프를 드러낸다. AOT(C++)는 제로 시작 지연 시간을 위해 개발 시간에 복잡성 비용(템플릿, 정적 타입)을 지불한다. JIT(Julia)는 첫 호출 지연 시간을 대가로 더 큰 개발 유연성과 동적 특수화를 제공한다. 선택은 애플리케이션의 성능 프로파일이 시작 시간에 의해 지배되는지, 아니면 장기 실행 계산에 의해 지배되는지에 따라 달라진다.

5. 종합 및 전략적 권장 사항

이 마지막 섹션에서는 이전 섹션들의 결과를 종합하여 사용자의 질문에 대한 명확하고 미묘한 답변을 제공하고 전략적 지침을 제시한다.

5.1 전제 재검토: C++ 수준 성능을 위한 조건

  • LLVM의 기준선: LLVM을 사용하는 것은 매우 높은 성능의 “기준선“을 설정한다. 깨끗하고 분석 가능한 LLVM IR을 생성할 수 있는 모든 언어는 빠른 코드를 생성할 것이다.2
  • 언어의 상한선: 궁극적인 성능 “상한선“은 언어의 의미론과 프론트엔드가 LLVM에 제공하는 정보의 질에 의해 결정된다.
  • 성능 동등성: 언어의 추상화가 제로 또는 네거티브 런타임 비용으로 컴파일될 수 있고, 언어가 최적화를 돕는 강력한 정적 보장(예: 에일리어싱)을 제공할 때 달성된다 (예: Rust).24
  • 성능 격차: 언어의 추상화가 LLVM 백엔드가 제거할 수 없는 피할 수 없는 런타임 오버헤드(예: 참조 카운팅 또는 동적 디스패치)를 도입할 때 발생한다 (예: 클래스를 사용하는 Swift).30

5.2 미묘한 평결: 도메인별 언어 강점

기술 리더의 언어 선택을 돕기 위해 다음 비교표와 분석을 제공한다.

특성C++RustSwift
모델명RAII / 수동 관리소유권 / 대여자동 참조 카운팅 (ARC)
주요 메커니즘소멸자 (Destructors)컴파일 타임 검사런타임 참조 카운팅
성능 오버헤드최소 / 예측 가능제로 / 컴파일 타임잠재적 런타임 오버헤드
개발자 부담높음중간 / 학습 곡선낮음

표 3: 메모리 관리 모델 및 성능 특성 비교

  • C++: 게임 엔진, 고빈도 거래 시스템과 같이 절대적인 최저 수준의 제어, 수동 메모리 관리, 성숙하고 방대한 생태계에 대한 접근이 필요한 애플리케이션에서 여전히 독보적인 리더이다.26
  • Rust: C++와 동등한 성능 요구사항을 가지면서 메모리 안전성과 동시성 정확성이 무엇보다 중요한 경우 이상적인 선택이다. 신뢰성이 타협 불가능한 시스템 프로그래밍, 임베디드 시스템, 네트워크 서비스에서 탁월하다.26
  • Swift: Apple 생태계 내의 애플리케이션 개발에 최적화된 선택이다. 모든 시나리오에서 마지막 한 방울의 성능을 짜내는 것보다 개발자 생산성, 안전성, 현대적인 구문을 우선시한다. UI 중심 애플리케이션이라는 목표 도메인에서는 충분하고도 남는 성능을 제공한다.32
  • Julia: 대화형 과학 및 수치 계산에 우월한 선택이다. 동적 언어의 사용 편의성과 계산 집약적인 작업에서 C++에 필적하는 성능을 결합하며, 첫 호출 지연 시간이 수용 가능한 경우에 적합하다.37

5.3 미래 전망: 성능의 수렴 궤적

LLVM 커뮤니티에서는 최적화를 개선하고 새로운 기능을 추가하기 위한 작업이 지속적으로 이루어지고 있다.44 또한, 현대 언어들이 서로의 기능을 채택하는 경향이 있다. Rust는 메타프로그래밍을 개선하고 있고, C++는 더 많은 안전 기능을 채택하고 있으며, Swift는 ARC 오버헤드를 완화하기 위해 더 명시적인 소유권 제어를 도입하고 있다.20

결론적으로, LLVM이 계속 발전하고 언어 설계자들이 “최적화기 친화적인” 추상화를 만드는 방법에 대해 더 많이 배우면서, 잘 설계된 LLVM 기반 언어와 C++ 간의 성능 격차는 계속해서 좁혀질 가능성이 높다. 언어 선택의 결정적인 요인은 순수한 성능 잠재력에서 안전성, 생산성, 생태계 적합성과 같은 요소로 점점 더 이동할 것이다.

6. Works cited

  1. The LLVM Project is a collection of modular and reusable compiler and toolchain technologies. - GitHub, accessed October 27, 2025, https://github.com/llvm/llvm-project
  2. LLVM - Wikipedia, accessed October 27, 2025, https://en.wikipedia.org/wiki/LLVM
  3. A Gentle Introduction to LLVM IR - mcyoung, accessed October 27, 2025, https://mcyoung.xyz/2023/08/01/llvm-ir/
  4. Understanding LLVM Passes and Their Types | CompilerSutra, accessed October 27, 2025, https://compilersutra.com/docs/llvm/intermediate/what_is_llvm_passes/
  5. Clang C Language Family Frontend for LLVM, accessed October 27, 2025, https://clang.llvm.org/
  6. Getting Started with the LLVM System — LLVM 22.0.0git documentation, accessed October 27, 2025, https://llvm.org/docs/GettingStarted.html
  7. Using LLVM/Clang for Compilation - openEuler Docs, accessed October 27, 2025, https://docs.openeuler.org/en/docs/24.03_LTS/docs/ApplicationDev/using-clang-for-compilation.html
  8. How is clang able to steer C/C++ code optimization? - Stack Overflow, accessed October 27, 2025, https://stackoverflow.com/questions/26715356/how-is-clang-able-to-steer-c-c-code-optimization
  9. Cross-compilation using Clang - LLVM, accessed October 27, 2025, https://clang.llvm.org/docs/CrossCompilation.html
  10. en.wikipedia.org, accessed October 27, 2025, https://en.wikipedia.org/wiki/LLVM#:~:text=LLVM%20currently%20supports%20compiling%20of,new%20frontends%20for%20many%20languages.
  11. A curated list of awesome LLVM (including Clang, etc) related resources. - GitHub, accessed October 27, 2025, https://github.com/learn-llvm/awesome-llvm
  12. LLVM’s Analysis and Transform Passes — LLVM 22.0.0git documentation, accessed October 27, 2025, https://llvm.org/docs/Passes.html
  13. Writing an LLVM Pass — LLVM 14.0.0 documentation, accessed October 27, 2025, https://releases.llvm.org/14.0.0/docs/WritingAnLLVMPass.html
  14. Recommended LLVM passes : r/Compilers - Reddit, accessed October 27, 2025, https://www.reddit.com/r/Compilers/comments/1hqmd7x/recommended_llvm_passes/
  15. LLVM Optimization Using C++ API - Stack Overflow, accessed October 27, 2025, https://stackoverflow.com/questions/31279623/llvm-optimization-using-c-api
  16. Compiler Optimization in a Language you Can Understand, accessed October 27, 2025, https://sbaziotis.com/compilers/compiler-opt.html
  17. CS 6120: Project 3: Write an LLVM Pass - Cornell: Computer Science, accessed October 27, 2025, https://www.cs.cornell.edu/courses/cs6120/2019fa/project/3/
  18. How llvm does O2 optimisation - clang - Stack Overflow, accessed October 27, 2025, https://stackoverflow.com/questions/42028859/how-llvm-does-o2-optimisation
  19. LLVM ir line highlighting now available on Godbolt Compiler Explorer : r/programming, accessed October 27, 2025, https://www.reddit.com/r/programming/comments/b44bhh/llvm_ir_line_highlighting_now_available_on/
  20. C++ vs Rust performance : r/cpp - Reddit, accessed October 27, 2025, https://www.reddit.com/r/cpp/comments/kyhark/c_vs_rust_performance/
  21. Rust vs C clang - Which programs are fastest? (Benchmarks Game), accessed October 27, 2025, https://benchmarksgame-team.pages.debian.net/benchmarksgame/fastest/rust.html
  22. Zero-Cost Abstractions in Rust: Power Without the Price - DockYard, accessed October 27, 2025, https://dockyard.com/blog/2025/04/15/zero-cost-abstractions-in-rust-power-without-the-price
  23. Zero-Cost Abstractions in Rust: Unlocking High Performance and Expressiveness, accessed October 27, 2025, https://davide-ceschia.medium.com/zero-cost-abstractions-in-rust-unlocking-high-performance-and-expressiveness-75c1c0d27291
  24. Zero Cost Abstractions - Without Boats, accessed October 27, 2025, https://without.boats/blog/zero-cost-abstractions/
  25. Learning Rust: Understanding Zero-Cost Abstraction with Filter and Map - RanveerSequeira, accessed October 27, 2025, https://ranveersequeira.medium.com/learning-rust-understanding-zero-cost-abstraction-with-filter-and-map-e967d09fff79
  26. Rust vs C++ Performance Analysis: Speed, Efficiency, and More - ParallelStaff, accessed October 27, 2025, https://parallelstaff.com/rust-vs-c-plus-plus-speed-benchmark/
  27. Memory Management via Ownership Concept Rust and Swift: Experimental Study - International Journal of Computer Applications, accessed October 27, 2025, https://www.ijcaonline.org/archives/volume183/number22/alhazmi-2021-ijca-921572.pdf
  28. Is C++ more performant than Rust?? : r/cpp - Reddit, accessed October 27, 2025, https://www.reddit.com/r/cpp/comments/17zaiu6/is_c_more_performant_than_rust/
  29. Swift vs C++ g++ - Which programs are fastest? (Benchmarks Game) - Debian, accessed October 27, 2025, https://benchmarksgame-team.pages.debian.net/benchmarksgame/fastest/swift-gpp.html
  30. Swift, C++ Performance - Primate Labs, accessed October 27, 2025, https://www.primatelabs.com/blog/2014/12/swift-performance/
  31. How much slower is Swift when compared with C++? - Reddit, accessed October 27, 2025, https://www.reddit.com/r/swift/comments/v8bs7h/how_much_slower_is_swift_when_compared_with_c/
  32. Is Swift better than C++? - Design Gurus, accessed October 27, 2025, https://www.designgurus.io/answers/detail/is-swift-better-than-c
  33. Memory Management in Rust and Swift | by Ryan Levick - Medium, accessed October 27, 2025, https://medium.com/@itchyankles/memory-management-in-rust-and-swift-8ecda3cdf5b7
  34. Swift VS C++/Rust? - Reddit, accessed October 27, 2025, https://www.reddit.com/r/swift/comments/sb7rul/swift_vs_crust/
  35. How viable is swift as a c++ alternative? - Reddit, accessed October 27, 2025, https://www.reddit.com/r/swift/comments/1c9d1ln/how_viable_is_swift_as_a_c_alternative/
  36. Benchmarking study: C++ < Fortran < Numba < Julia < Java < Matlab < the rest - Google Groups, accessed October 27, 2025, https://groups.google.com/g/julia-users/c/dl_ysr1TdXE
  37. Comparison of C/C++, Python and Julia language performance for a set of… - ResearchGate, accessed October 27, 2025, https://www.researchgate.net/figure/Comparison-of-C-C-Python-and-Julia-language-performance-for-a-set-of-short-algorithms_fig3_374476055
  38. A Benchmarking showing that Julia can be as fast as C/C++/Rust - Reddit, accessed October 27, 2025, https://www.reddit.com/r/Julia/comments/and0xm/a_benchmarking_showing_that_julia_can_be_as_fast/
  39. go directly to binary, do not pass C. Compilation of Julia code for deployment in model-based engineering. - arXiv, accessed October 27, 2025, https://arxiv.org/html/2502.01128v1
  40. AOT vs JIT Comilation : r/Compilers - Reddit, accessed October 27, 2025, https://www.reddit.com/r/Compilers/comments/19ctf7p/aot_vs_jit_comilation/
  41. Julia using AOT compiler? - General Usage - Julia Programming …, accessed October 27, 2025, https://discourse.julialang.org/t/julia-using-aot-compiler/7232
  42. What do you guys think of Julia in terms of speed? : r/cpp_questions - Reddit, accessed October 27, 2025, https://www.reddit.com/r/cpp_questions/comments/le3gzx/what_do_you_guys_think_of_julia_in_terms_of_speed/
  43. Comparing AOT and JIT Compilers: Understanding the Differences and Making an Informed Choice — Programming concepts - Amin Nezampour, accessed October 27, 2025, https://aminnez.com/programming-concepts/jit-vs-aot-compiler-pros-cons
  44. The LLVM Compiler Infrastructure Project, accessed October 27, 2025, https://llvm.org/OpenProjects.html