Rust의 속도 장점
코딩을 하면서 항상 느끼는 것이지만, “어떻게 빠르고 오류 없는 프로그램을 개발할 수 있을까?”는 개발자들의 가장 큰 고민 중 하나라고 생각합니다. 개발을 진행하면서 오류는 항상 발생하기 때문에, 오류를 100% 없애기보다는 최대한 줄이는 방향으로 나아가는 것이 개발 시간을 단축하는 방법이라고 믿습니다. Java나 C# 같은 언어로 처음 개발을 시작하다가 즉각적인 레이턴시 요구사항으로 인해 개발을 진행하다 보면, C++이 필연적으로 필요한 경우가 발생합니다. C++은 작성 후 컴파일하면 기계어로 번역되어 프로그램이 실행되기 때문에 매우 빠른 성능을 보여주지만, 프로그램의 흐름을 제대로 파악하지 않고 사용한다면 많은 오류를 발생시킬 수 있습니다.
대표적으로 개발자들이 겪는 C++ 컴파일 및 디버깅 오류로는 다음과 같은 문제들이 있습니다:
1. 개발자에게 자주 발생하는 C++ Compile/Debugging 오류
- 메모리 오류 : C++에서는 메모리 누수, dangling pointers 및 메모리 해제 후에 포인터를 사용하는 등의 일반적인 메모리 관리 오류가 발생할 수 있다.
- 스레딩 오류 : C++에서는 경쟁 조건, 데드락 및 스레드 안전성 문제와 같은 다중 스레딩 오류가 발생할 수 있다.
- 안전하지 않은 형 변환 및 포인터 조작 : C++에서는 포인터 캐스팅 및 원시 포인터 조작과 같은 위험한 형 변환을 수행할 수 있으며, 이는 메모리 오류로 이어질 수 있다.
- Null 포인터 및 널 참조 : C++에서는 null 포인터 및 널 참조로 인한 오류가 발생할 수 있다.
- 안전하지 않은 동작 : C++에서는 배열 경계 초과, 포인터 산술 오버플로우 및 다른 안전하지 않은 작업으로 인한 예기치 않은 동작이 발생할 수 있다.
이러한 문제가 자주 발생하다 보니 C++ 개발자들은 앞서 언급한 여러 문제들에 대해 끊임없이 신경 써야 하며, 그 결과 개발 리소스의 상당 부분이 소모되기 마련입니다. 이러한 문제점을 해결하기 위해 모질라 재단은 2010년 7월경 Rust라는 새로운 프로그래밍 언어를 발표하였습니다.
Rust 언어는 위에서 언급한 문제들을 다음과 같이 해결하였습니다.
2. Rust의 문제 해결법
- 메모리 오류 : Rust는 컴파일 시간에 메모리 안전성을 강제하므로 이러한 종류의 오류를 방지한다.
- 스레딩 오류 : Rust는 안전한 다중 스레딩을 지원하기 위한 기능을 제공하여 이러한 종류의 오류를 방지한다.
- 안전하지 않은 형 변환 및 포인터 조작 : Rust는 안전하지 않은 형 변환을 허용하지만, 이를 안전하게 수행할 수 있는 방법을 제공한다.
- Null 포인터 및 널 참조 : Rust는 Option 및 Result와 같은 열거형 타입을 사용하여 null 포인터를 피하고, 널 참조 오류를 방지한다.
- 안전하지 않은 동작 : Rust는 안전하지 않은 작업을 엄격하게 제어하여 이러한 종류의 오류를 방지한다.
Rust는 여러 특장점 덕분에 차세대 언어로 개발자들 사이에서 점차 주목받고 있습니다.
그러나 1983년 덴마크에서 Bjarne Stroustrup에 의해 발표된 C++는 오랜 기간 동안 사용되며 방대한 레퍼런스와 라이브러리를 축적해왔기에,
2024년 현재까지 Rust가 C++의 입지를 완전히 대체하지 못하고 있는 것이 현실입니다.
개인적으로는 미래에 Rust가 주류 언어로 자리잡아 보다 안전하고 효율적인 개발 환경을 제공할 수 있기를 기대합니다.
테스트 PC 사양
- CPU : AMD 6800H
- RAM : Micron DDR5 4800Mhz 16GB X 2
- 저장장치 : Samsung NVME SSD PM9A1 1TB
Rust와 C++의 속도 비교 결과
1. 100만개의 리스트에 100만개의 랜덤 데이터 넣기
- Rust : 63.29초
- C++ : 170.32초
2. 100만개의 데이터 병합 정렬 (Merge Sort)
- Rust : 463.89ms
- C++ : 978.23ms
3. 100만 자리의 피보나치 수열 계산
- Rust : 67.85초
- C++ : 20.32초
Rust 소스
1. 100만개의 리스트에 100만개의 랜덤 데이터 넣기
use rand::Rng;
use std::time::{Instant};
// 랜덤 데이터를 생성하여 벡터에 저장하는 함수
fn generate_random_data(size: usize) -> Vec<u32> {
let mut data = Vec::with_capacity(size);
let mut rng = rand::thread_rng();
for _ in 0..size {
// 0부터 99999 사이의 랜덤 값 생성하여 벡터에 추가
data.push(rng.gen_range(0..100000));
}
data
}
// 100만개의 리스트에 대해 100만번의 작업을 수행하는 함수
fn process_list(data_list: &[u32]) {
for &data in data_list {
// 여기서 작업을 수행
// 예시로 각 요소를 출력하는 작업을 수행
println!("{}", data);
}
}
fn main() {
// 시작 시간 측정
let start_time = Instant::now();
// 100만개의 랜덤 데이터 생성
let data_list = generate_random_data(1_000_000);
// 데이터 처리
process_list(&data_list);
// 종료 시간 측정 및 실행 시간 출력
let end_time = Instant::now();
let duration = end_time - start_time;
println!("테스트 시간: {:?}", duration);
}
(rand = 0.9.0-alpha.1)
2. 100만개의 데이터 병합 정렬 (Merge Sort)
use std::time::Instant;
fn merge(arr: &mut [i32], temp: &mut [i32], left: usize, mid: usize, right: usize) {
let mut i = left;
let mut j = mid + 1;
let mut k = left;
while i <= mid && j <= right {
if arr[i] <= arr[j] {
temp[k] = arr[i];
i += 1;
} else {
temp[k] = arr[j];
j += 1;
}
k += 1;
}
while i <= mid {
temp[k] = arr[i];
i += 1;
k += 1;
}
while j <= right {
temp[k] = arr[j];
j += 1;
k += 1;
}
for idx in left..=right {
arr[idx] = temp[idx];
}
}
fn merge_sort(arr: &mut [i32], temp: &mut [i32], left: usize, right: usize) {
if left < right {
let mid = left + (right - left) / 2;
merge_sort(arr, temp, left, mid);
merge_sort(arr, temp, mid + 1, right);
merge(arr, temp, left, mid, right);
}
}
fn main() {
let mut arr = vec![0; 1000000];
let mut temp = vec![0; 1000000];
// 배열 초기화 (랜덤 또는 원하는 값으로 초기화)
let start_time = Instant::now();
let arr_len = arr.len();
merge_sort(&mut arr, &mut temp, 0, arr_len - 1);
let duration = start_time.elapsed();
println!("병합 정렬 실행 시간: {:?}", duration);
}
3. 100만개의 리스트에 100만개의 랜덤 데이터 넣기
use std::time::Instant;
use num_bigint::BigInt;
use num_traits::{Zero, One};
fn fibonacci(n: usize) -> BigInt {
let mut a = BigInt::zero();
let mut b = BigInt::one();
for _ in 0..n {
let tmp = a.clone();
a = b.clone();
b += tmp;
}
a
}
fn main() {
let n = 1000000; // 계산하고자 하는 피보나치 수열의 인덱스
let start_time = Instant::now();
let result = fibonacci(n);
let end_time = start_time.elapsed();
println!("피보나치 수열 계산에 걸린 시간: {:?}", end_time);
}
C++ 소스
1. 100만개의 리스트에 100만개의 랜덤 데이터 넣기
#include <iostream>
#include <vector>
#include <cstdlib>
#include <ctime>
#include <chrono>
// 랜덤 데이터를 생성하여 벡터에 저장하는 함수
std::vector<int> generateRandomData(int size) {
std::vector<int> data;
// 랜덤 시드 설정
std::srand(std::time(nullptr));
for (int i = 0; i < size; ++i) {
// 0부터 99999 사이의 랜덤 값 생성하여 벡터에 추가
data.push_back(std::rand() % 100000);
}
return data;
}
// 100만개의 리스트에 대해 100만번의 작업을 수행하는 함수
void processList(std::vector<int>& dataList) {
for (int i = 0; i < dataList.size(); ++i) {
// 여기서 작업을 수행
// 예시로 각 요소를 출력하는 작업을 수행
std::cout << dataList[i] << std::endl;
}
}
int main() {
// 시작 시간 측정
auto start = std::chrono::steady_clock::now();
// 100만개의 랜덤 데이터 생성
std::vector<int> dataList = generateRandomData(1000000);
// 데이터 처리
processList(dataList);
// 종료 시간 측정 및 실행 시간 출력
auto end = std::chrono::steady_clock::now();
std::chrono::duration<double> elapsedSeconds = end - start;
std::cout << "테스트 시간: " << elapsedSeconds.count() << " seconds" << std::endl;
return 0;
}
2. 100만개의 데이터 병합 정렬 (Merge Sort)
#include <iostream>
#include <vector>
#include <algorithm>
#include <chrono>
#include <random>
void merge(std::vector<int>& arr, std::vector<int>& temp, int left, int mid, int right) {
int i = left;
int j = mid + 1;
int k = left;
while (i <= mid && j <= right) {
if (arr[i] <= arr[j]) {
temp[k] = arr[i];
i++;
} else {
temp[k] = arr[j];
j++;
}
k++;
}
while (i <= mid) {
temp[k] = arr[i];
i++;
k++;
}
while (j <= right) {
temp[k] = arr[j];
j++;
k++;
}
for (int idx = left; idx <= right; idx++) {
arr[idx] = temp[idx];
}
}
void mergeSort(std::vector<int>& arr, std::vector<int>& temp, int left, int right) {
if (left < right) {
int mid = left + (right - left) / 2;
mergeSort(arr, temp, left, mid);
mergeSort(arr, temp, mid + 1, right);
merge(arr, temp, left, mid, right);
}
}
int main() {
std::random_device rd;
std::mt19937 gen(rd());
std::uniform_int_distribution<> dis(0, 1000000);
std::vector<int> arr(1000000);
std::vector<int> temp(1000000);
// 배열 초기화 (랜덤 또는 원하는 값으로 초기화)
for (int i = 0; i < 1000000; ++i) {
arr[i] = dis(gen);
}
auto start_time = std::chrono::high_resolution_clock::now();
mergeSort(arr, temp, 0, arr.size() - 1);
auto end_time = std::chrono::high_resolution_clock::now();
auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(end_time - start_time);
std::cout << "병합 정렬 실행 시간: " << duration.count() << " milliseconds" << std::endl;
return 0;
}
3. 100만개의 리스트에 100만개의 랜덤 데이터 넣기
#include <iostream>
#include <chrono>
#include <boost/multiprecision/cpp_int.hpp>
namespace mp = boost::multiprecision;
using bigint = mp::cpp_int;
using namespace std::chrono;
bigint fibonacci(int n) {
bigint a = 0, b = 1;
for (int i = 0; i < n; ++i) {
bigint tmp = a;
a = b;
b += tmp;
}
return a;
}
int main() {
int n = 1000000; // 계산하고자 하는 피보나치 수열의 인덱스
auto start_time = high_resolution_clock::now();
bigint result = fibonacci(n);
auto end_time = high_resolution_clock::now();
std::cout << "피보나치 수열 계산에 걸린 시간: " << duration_cast<milliseconds>(end_time - start_time).count() << " ms" << std::endl;
return 0;
}
마지막으로
실험 결과를 살펴보면, Rust와 C++ 모두 특정 조건하에서 뛰어난 성능을 보이는 사례가 관측되고 있습니다.
상황에 따라 Rust가 더 빠른 결과를 나타내기도 하고, 반대로 C++이 우위를 점하는 경우도 있는데,
이는 단순히 언어 자체의 차이뿐만 아니라, 각 언어의 컴파일러 최적화, 런타임 환경, 그리고 코드 작성 스타일 등 복합적인 요인이 작용한 결과로 판단됩니다.
앞으로 시간적 여유가 있을 때, 이러한 성능 차이의 원인을 다각도로 분석해보고자 합니다.
예를 들어, 동일한 알고리즘과 데이터 구조를 Rust와 C++로 구현한 후,
각 컴파일러의 최적화 옵션, 메모리 할당 전략, 그리고 런타임 오버헤드 등을 비교하는 정밀한 실험을 진행하려고 합니다.
또한, 실제 애플리케이션 개발 현장에서 발생하는 다양한 변수들을 고려하여, 어느 조건에서 어떤 언어가 더 유리한지에 대한 실증적 자료를 확보하는 것이 중요하다고 생각됩니다.
본 분석은 단순한 성능 비교를 넘어, 두 언어의 설계 철학과 개발 도구, 그리고 생태계 전반에 걸친 차이점을 이해하는 데 기여할 것으로 기대됩니다.
이를 통해 개발자들이 프로젝트의 특성과 요구 사항에 따라 최적의 언어 선택을 할 수 있도록, 보다 근거 중심의 가이드라인을 마련하는 데 도움이 되고자 합니다.
혹시 글의 내용 중에 잘못된 부분이나 부정확한 정보가 있다면 언제든지 피드백을 주시기 바랍니다.
여러분의 소중한 의견을 반영하여 보다 완성도 높은 결과를 공유할 수 있도록 지속적으로 수정·보완해 나가겠습니다.
'Dev > Rust' 카테고리의 다른 글
Rust vs Go 차이점 (0) | 2025.03.23 |
---|---|
Rust와 C/C++의 차이점에 대한 심층 분석 (1) | 2025.03.17 |