그냥 간단한 블로그 포스팅용 실험 입니다.
SPSC(Single Producer Single Consumer) Lock-Free Queue의 최적화 단계별 벤치마크 비교.
Rust와 C로 동일한 4단계 최적화를 구현하고, 정적/동적 버퍼 할당에 따른 성능 차이를 측정한다.
| 단계 | 이름 | 설명 |
|---|---|---|
| 1 | basic | modulo 연산, 매번 atomic load, 패딩 없음 |
| 2 | padded | + 캐시라인 패딩 (false sharing 방지) |
| 3 | cached | + cached index (atomic load 절감) |
| 4 | masked | + 비트마스킹 (modulo -> bitwise AND) |
각 단계를 동적 버퍼(런타임 크기)와 정적 버퍼(컴파일 타임 고정 크기) 두 가지로 벤치마크한다.
lockfreequeue/
├── src/
│ ├── main.rs # 벤치마크 + 테스트
│ ├── buffer.rs # Buffer trait + Static<T,N> / Dynamic<T>
│ ├── queue.rs # 4모드 통합 구현 (basic/padded/cached/masked)
│ └── queue_proto.rs # 초기 프로토타입 구현
├── C/
│ ├── include/
│ │ ├── lockfree_queue.h # 동적 큐 4종
│ │ └── lockfree_queue_static.h # 정적 큐 매크로 생성
│ ├── bench_main.c # C 벤치마크
│ └── Makefile
├── Cargo.toml
├── BENCHMARK.md # 벤치마크 결과
└── sample.md # 이전 블로그 포스트
Buffer<T> trait으로 정적/동적 버퍼를 추상화하여, 같은 큐 코드로 두 가지 모드를 지원한다:
use buffer::{Static, Dynamic};
// 정적 — 컴파일러가 capacity를 상수로 알고 modulo 자동 최적화
let (mut p, mut c) = queue::cached::Queue::<u64, Static<u64, 1024>>::new().split();
// 동적 — 같은 push/pop 코드, 런타임 capacity
let (mut p, mut c) = queue::cached::Queue::<u64, Dynamic<u64>>::new(1024).split();
// 사용법은 동일
p.push(42);
let val = c.pop(); // Some(42)Rust의 제네릭 + const generic으로 monomorphization되므로, trait 추상화에 의한 런타임 오버헤드가 없다.
C에서는 동일한 효과를 매크로 코드 생성으로 달성한다:
DEFINE_ALL_STATIC_QUEUES(1024) // SBasicQueue_1024, SCachedQueue_1024, ... 생성- Rust: rustc (edition 2024)
- C: gcc (C11 지원), pthread
- OS: Linux 또는 Windows (MSYS2/MinGW-w64)
# 벤치마크 (release 모드, 5회 평균)
cargo run --release
# 테스트 (8개: 동적 4종 + 정적 4종)
cargo test
# Miri 동시성 안전성 검증
cargo +nightly miri testcd C
# 직접 컴파일
gcc -std=c11 -O3 -Wall -pthread -o bench bench_main.c
# 또는 Makefile 사용 (make 설치 시)
make bench
# 실행
./bench- 패턴: SPSC (Producer 1스레드 -> Consumer 1스레드, spin-loop 대기)
- 아이템:
u64/uint64_t, 10,000,000개 - 큐 용량: 64, 256, 1024, 4096, 65536
- 측정: 5회 실행 평균 (M ops/sec, ns/op)
- Rust 추가: rtrb 크레이트와 비교
자세한 결과는 BENCHMARK.md 참조.
- cached index가 가장 큰 영향: ~4배 향상
- 비트마스킹 추가로 ~3배 향상
- 정적 버퍼가 동적 대비 3~5배 빠름 (컴파일러 modulo 자동 최적화)
- 정적 cached > 동적 masked: 컴파일 타임 최적화가 수동 비트마스킹을 능가
- C static cached (cap=65536): ~988 M/s (거의 1 GHz ops)
MIT