6개월 전 이 포스트로 Wave와 C, Rust의 벤치마크를 해보았다.
그로부터 6개월이 지난 오늘 상당히 발전된 Wave로 다시 한번 벤치마킹을 해보았다.
이 벤치마킹에 사용된 Wave 버전은 개발중인 v0.1.7-pre-beta 중 하나로 이 커밋 이상 유효하다.
이 벤치마크에 사용된 모든 소스코드는 공개가 되어있다.
벤치마킹을 할때 Wave, Rust C 모두 LLVM 백엔드를 전제로 벤치마킹을 하였다.
체크:

위 그래프는 무엇을 말하냐면 실제로 CPU가 몇 사이클을 썼는가를 보여준다.
이 그래프는 컴파일러 최적화가 얼마나 잘 먹히는가를 보여준다.

상대 지표는 C를 1.0으로 두고, 다른 언어가 C보다 몇 배 느린지를 나타낸 그래프다.
즉:
Wave / C = Wave_cycles ÷ C_cycles
Rust / C = Rust_cycles ÷ C_cycles
이 지표를 보면 Wave는 언어로써 상당히 경쟁력이 있다는걸 볼 수 있다.
이것은 Rust가 안전성과 추상화 비용이 있기 때문에 더 느린 결과를 불러온다고 볼 수 있다.
이번 벤치마크 결과를 정리하면 다음과 같다.
이는 Wave가 저수준 코드 생성 과정에서
불필요한 추상화 비용을 거의 발생시키지 않으면서도,
C에 가까운 실행 모델을 유지하고 있음을 보여준다.
아직 Wave는 pre-beta 단계의 개발 중인 언어이며,
자체 최적화 파이프라인이나 LTO, PGO 같은 고급 최적화 기능도 도입되지 않은 상태다.
그럼에도 불구하고 이 정도의 성능을 보여준다는 점은
언어 설계와 컴파일러 구조가 올바른 방향으로 가고 있다는 하나의 근거가 된다.
이 벤치마크의 핵심은 단순히
“C, Rust, Wave 중 누가 더 빠르냐”에 있지 않다.
세 언어는 애초에 지향하는 목표가 다르다.
이번 결과는 Wave가 그 지향점에 맞게,
추가적인 런타임 비용 없이 매우 직관적인 LLVM IR을 생성하고 있음을 보여준다.
즉 Wave는
성능을 얻기 위해 언어를 과도하게 단순화하지도 않고,
안전성을 위해 성능을 크게 희생하지도 않는
그 중간 지점을 실제 수치로 증명하고 있다고 볼 수 있다.
이번 테스트에서 Rust보다 Wave가 더 적은 사이클을 사용한 이유는
Rust가 “느린 언어”이기 때문이라기보다는,
Wave가 의도적으로 더 단순한 실행 모델을 선택했기 때문이다.
Rust는 안전성과 추상화를 위해
다양한 컴파일 타임 보장과 런타임 개념을 제공한다.
이러한 보장들은 대부분의 실제 애플리케이션에서 충분히 가치 있지만,
아주 단순한 저수준 루프에서는 오버헤드로 작용할 수 있다.
반면 Wave는
명시적인 타입, 단순한 메모리 모델,
그리고 최소한의 암묵적 동작을 기반으로 설계되었고,
그 결과 LLVM이 보다 공격적으로 최적화할 수 있는 IR을 생성한다.
이 차이는 특히 O1 이후 최적화 단계에서
성능 격차로 분명하게 드러났다.
이번 벤치마크는 Wave의 “최종 성능”을 보여주는 결과가 아니다.
현재 시점에서 Wave가 어디쯤 와 있는지를 보여주는 하나의 스냅샷에 가깝다.
향후 Wave에는 다음과 같은 개선이 예정되어 있다.
이러한 요소들이 더해진다면,
Wave의 성능 특성은 지금보다 더욱 C에 가까워질 가능성이 크다.
앞으로도 동일한 벤치마크를 주기적으로 반복하며,
Wave가 어떻게 진화해 나가는지를 계속 기록해 나갈 예정이다.