Rust는 C보다 빠를까?
(steveklabnik.com)- 러스트와 C의 성능 비교는 “모든 조건이 동일하다”는 전제의 정의에 따라 달라지는 복잡한 문제임
- 인라인 어셈블리의 경우 두 언어 모두 동일한 어셈블리 코드를 생성할 수 있어, 언어 자체의 속도 차이는 없음
-
구조체 메모리 배치에서는 러스트가 필드 재정렬을 통해 더 작은 크기를 가질 수 있으나,
#[repr(C)]속성으로 C와 동일한 레이아웃도 가능함 - 런타임 검사와 개발자 행태 차이로 인해 실제 프로젝트에서는 코드 구조와 성능이 달라질 수 있음
- 결론적으로 언어 자체의 한계로 인한 성능 차이는 없으며, 프로젝트와 개발자 요인에 따라 결과가 달라짐
문제 제기와 전제의 모호함
- Reddit에서 제기된 “같은 조건이라면 Rust가 C보다 빠를 수 있는가” 라는 질문이 출발점
- “모든 조건이 동일함” 이라는 표현 자체가 언어 비교에서 매우 정의하기 어려운 개념임
- 성능 비교는 언어 차이뿐 아니라 코드 형태, 개발 판단, 컴파일러 최적화에 따라 달라짐
인라인 어셈블리 비교
- 러스트는 언어 차원에서 인라인 어셈블리를 지원하며, C는 컴파일러 확장 기능으로 이를 구현
- 두 언어 모두
rdtsc명령을 사용하는 동일한 예제를 작성할 수 있음 -
rustc 1.87.0과clang 20.1.0에서 생성된 어셈블리는 완전히 동일한 형태로 출력됨
- 두 언어 모두
- 이 사례는 언어의 성능 차이를 보여주지는 않지만, 러스트가 C와 동일한 수준의 저수준 제어를 수행할 수 있음을 입증
구조체 레이아웃 차이
- 러스트 구조체는 필드 재정렬을 통해 메모리 사용을 최적화할 수 있음
- 예시에서 러스트 구조체는 16바이트, 동일한 C 구조체는 24바이트로 계산됨
- C에서는 필드 순서를 수동으로 변경해야 동일한 크기를 얻을 수 있음
- 러스트에서
#[repr(C)]속성을 사용하면 C와 동일한 메모리 레이아웃을 강제할 수 있음
사회적·개발자 요인
-
러스트의 안전성 검사 덕분에 개발자가 더 공격적인 최적화를 시도할 수 있는 사례가 있음
- Mozilla의 Stylo 프로젝트에서는 C++로 두 차례 병렬화 시도가 실패했으나, 러스트로는 성공적으로 구현됨
- 동일한 프로젝트라도 언어와 개발자 숙련도에 따라 결과 코드의 성능과 안정성이 달라질 수 있음
- 초급 개발자와 전문가, 언어 숙련도에 따라 “같은 작업”의 결과가 다르므로, 단순 비교는 어려움
컴파일 타임과 런타임 검사
- 러스트의 많은 안전성 검사는 컴파일 타임에 수행되지만, 일부는 런타임 검사로 남음
- 예를 들어
array[0]접근 시 러스트는 경계 검사를 수행하지만, C는 수행하지 않음 - 러스트에서
get_unchecked()를 사용하면 C와 동일한 동작을 얻을 수 있음
- 예를 들어
- 컴파일러가 안전성을 증명할 수 있는 경우, 두 언어 모두 검사를 최적화로 제거할 수 있음
- 이러한 차이는 코드 작성 방식에 영향을 주며, 결과적으로 성능 차이를 유발할 수 있음
결론
- C가 “가장 빠른 언어”라고 가정하더라도, 러스트가 동일한 수준의 성능을 낼 수 없는 이유는 없음
- 언어 자체의 한계보다는 프로젝트 특성, 개발자 역량, 시간 제약 등 외부 변수가 성능 차이를 결정
- 따라서 “러스트가 C보다 빠른가?”라는 질문은 언어 비교보다는 엔지니어링 맥락의 문제로 해석됨
댓글과 토론
임베디드에서는 하드웨어 캐시 라인사이즈까지 고려해서 코딩하죠. 프로그래머가 극한의 최적화를 언어 위에서 어디까지 할수있냐랑 표준라이브러리와 컴파일러 성능 문제일텐데요 어차피 둘다 저수준을 지원하기 때문에 약간의 오버헤드 차이는 미미한 수준일 것 같네요. 그래서 그리 의미있는 논쟁은 아닌 듯.. 극한의 최적화가 필요하면 결국 인간 개입이 필요합니다. 컴파일러가 생각만큼 완벽하지 않아서요
ffmpeg아조씨들은 rust가 c보다 빠른건 아니다 생각하는것 같긴하네요 ㅋㅋ https://www.memorysafety.org/blog/rav1d-perf-bounty/
Hacker News 의견들
-
요약하자면, 최대 속도는 거의 같지만, 실제 코드에서는 차이가 큼
특히 멀티스레딩이 큰 변수임. Rust는 스레드를 쓰든 안 쓰든 모든 전역 변수가 스레드 안전해야 하고, 빌림 검사기(borrow checker)가 메모리 접근을 공유 또는 변경 중 하나로 제한함
그래서 Rust에서는 멀티스레드 코드 작성이 거의 기본처럼 되어 있음. 반면 C에서는 스레드 생성 자체가 플랫폼 호환성 문제나 디버깅 리스크 때문에 부담이 큼- 솔직히 말하면, 이런 차이는 주로 표준 라이브러리의 편의성 차이 때문이라고 생각함
C에서 스레드 빌드가 어렵진 않지만 Rust의std::thread::spawn(move || { ... });보다는 번거로움
메모리 안전성보다는 언어의 동시성 모델이 더 큰 영향을 줌. Go는 메모리 안전하지 않아도go f()로 쉽게 병렬화함
개인적으로는 Go에서 heisenbug를 더 자주 봤음 - 멀티스레딩 외에도 Rust의 타입 시스템이 더 많은 정보를 제공해서 최적화 여지가 있을지 궁금함
- 사실
#pragma omp for한 줄이면 C에서도 간단히 병렬화할 수 있음 - Linux에서 멀티스레딩하려면 왜 TBB 링크가 필요한지 아직도 혼란스러움. 기본 포함되어야 한다고 생각함
- 스레드를 마구 추가하면 에너지 사용량과 경쟁 상태는 어떻게 되는지도 고려해야 함
- 솔직히 말하면, 이런 차이는 주로 표준 라이브러리의 편의성 차이 때문이라고 생각함
-
Rust의 traits 덕분에 더 빠르고 유연한 추상화를 만들 수 있음
C에서는 매크로나 함수 포인터로 흉내낼 수 있지만, Rust에서는 호출자가 동적 디스패치나 정적 디스패치를 선택할 수 있음
임베디드 환경에서는 함수 포인터가 캐시를 망치고 성능을 깎는데, Rust traits는 인라인 최적화가 가능해서 훨씬 효율적임- ELF 해킹도 좋아하지만, 성능을 위해선 링커와 ABI, 바이너리 포맷을 잘 알아야 함
Rust든 C든 결국 바이트 단위로 다뤄야 하며, 요즘은 바이너리 패칭 도구도 좋아져서 활용하기 쉬움 - 물론 Rust에서도 함수 시그니처에
Box<dyn Trait>를 쓰면 호출자가 동적 디스패치를 강제당함
impl Trait을 쓰면 호출자에게 선택권이 남음 - 다만 traits를 많이 쓰면 빌드 타임 증가가 눈에 띔. 복잡한 trait일수록 컴파일이 느려짐
- C식 접근은 아예 추상화를 피하는 것임
- ELF 해킹도 좋아하지만, 성능을 위해선 링커와 ABI, 바이너리 포맷을 잘 알아야 함
-
개인적으로 Rust, C, C++은 거의 같은 저수준 언어 계열이라 성능 차이는 미미하다고 봄
Rust의 엄격한 aliasing 규칙은 최적화에 유리하고, C/C++의 UB(정의되지 않은 동작) 은 성능을 위해 존재함
또 Rust와 C++의 제네릭은 C보다 훨씬 강력해서, 예를 들어qsort()대신 템플릿 기반 정렬은 인라인 최적화가 쉬움- C++의 템플릿은 여전히 Rust보다 표현력이 강하지만 점점 격차가 줄고 있음
- 사실 이건 언어보다는 표준 라이브러리 설계 문제임. C에서도 직접 인라인 가능한 정렬 함수를 만들 수 있음
- Rust의 정수 오버플로우 처리는 플랫폼 기본 동작을 따르므로, 최적화에 따라 결과가 달라질 수 있음
- 언어는 단지 행동을 기술하는 수단일 뿐, 실제 속도는 컴파일러와 런타임 환경에 달려 있음
- 세 언어의 성능 차이는 결국 노력 대비 효율의 문제임. C는 빠르지만 개발 비용이 크고, Rust는 그 중간쯤임
-
이런 언어 간 속도 논쟁은 대부분 무의미하다고 생각함
언어 자체보다 컴파일러 구현이 성능을 좌우함- 그래도 언어 설계는 최적화 가능성에 큰 영향을 줌. ‘충분히 똑똑한 컴파일러’는 아직 신화에 불과함
-
Rust, C, C++은 모두 낮은 수준의 언어지만, “빠르다”는 말의 정의가 중요함
전문가가 최적화한 코드의 최고 속도를 말하는 건지, 아니면 평균적인 개발자가 예산 내에서 빠른 코드를 짤 확률을 말하는 건지 다름- 실제로는 컴파일러의 최적화 능력이 핵심임. Rust의 메모리 의미론이 더 풍부해서 컴파일러가 최적화 정보를 더 얻을 수 있음
하지만 수작업 최적화를 하면 언어 차이는 거의 사라짐
다만 Rust는 빠른 코드 작성이 더 쉬운 언어라는 점에서 약간의 우위가 있음 - 대부분 사람들은 “평균적인 개발자가 빠른 코드를 짤 수 있는 언어”라는 정의를 씀
- 언어 설계자는 ‘이 언어의 전형적인 코드가 얼마나 빠를까’ 를 염두에 두므로, 이 질문은 의미 있다고 생각함
- 실제로는 컴파일러의 최적화 능력이 핵심임. Rust의 메모리 의미론이 더 풍부해서 컴파일러가 최적화 정보를 더 얻을 수 있음
-
Rust의 장점은 멀티스레딩과 스택 할당이라고 생각했음
소유권 모델 덕분에 C/C++보다 스택에 더 많은 걸 올릴 수 있어 malloc/free 오버헤드를 줄임- 맞음, 하지만 이번 논의는 그런 구체적 차이보다 언어 설계 관점의 개념적 비교에 초점을 두었음
이런 주제는 감정적으로 논쟁이 많아서, 구체적 수치보다는 사고방식의 차이를 다루고 싶었음
- 맞음, 하지만 이번 논의는 그런 구체적 차이보다 언어 설계 관점의 개념적 비교에 초점을 두었음
-
언어의 “속도”를 논할 때는 두 가지를 봐야 함
- 언어가 프로그램에 어떤 비용을 주입하는가
- 언어가 어떤 최적화를 가능하게 하는가
Rust와 C는 런타임 체크가 거의 없어서 Python이나 JS보다 빠름
다만 Rust는 aliasing 정보를 더 잘 전달해 최적화 여지가 있음
디버그 모드에서는 Ruby만큼 느리지만, 릴리스 모드에서는 C 수준의 속도를 냄
- 이 관점이 마음에 듦. C++의 zero-cost abstraction 개념과도 연결됨
- JS도 많이 최적화되어 있지만, 여전히 Rust/C보다 약간 느림
- 또 하나의 질문은 “평범한 개발자가 쓸 때 얼마나 빠른가”임. 언어가 기본적으로 빠른 코드를 유도하는지가 중요함
-
C보다 C++이나 Rust가 컴파일 타임 기능이 풍부해서 빠른 코드를 쓰기 쉬움
예를 들어 이 코드는 C에서는 거의 불가능함- 다른 예로 CTRE나 fmt의 compile API가 있음.
C에서는 re2c 같은 외부 도구가 필요함
- 다른 예로 CTRE나 fmt의 compile API가 있음.
-
어셈블리는 C 표준의 일부가 아니므로 Rust와 직접 비교하기 어렵고, Rust는 오히려 GCC 프로젝트에 더 가까운 성격임
-
어떤 언어가 “빠르다”는 건 결국 구현과 맥락에 따라 달라짐
언어 자체의 속도보다, 컴파일러와 하드웨어의 조합이 더 큰 영향을 미침