Lobsters

Debunking "Faster than Rust": Malicious Benchmarks in Tov

“Rust보다 빠르다”는 주장 반박: Tov의 악의적 벤치마크

Tov의 벤치마크가 Rust보다 빠르다고 주장하지만, 세 사례에서 비교 코드와 선택한 자료구조가 서로 달랐다고 비판합니다. 개선한 Rust 코드에서는 Tov와 비슷하거나 더 나은 성능이 나왔지만, 댓글은 동적 디스패치와 해시 함수의 선택에 따라 비교의 의미가 달라진다고 지적합니다.

에디터 노트

벤치마크 코드가 서로 다른 점은 분명하지만, 그 차이만으로 개발자의 악의적 의도를 입증하지는 못합니다. dispatch에서는 동적 디스패치와 열거형이 지원하는 타입 범위가 달라 단순히 더 빠른 숫자만 비교하기 어렵다는 댓글 지적이 타당합니다. map_insert도 표준 `HashMap`의 기본 해시 함수와 `ahash`를 비교하면서 키 생성 방식까지 바꿨습니다. 따라서 이 수치는 Rust 프로그램을 더 빠르게 만드는 선택지를 보여주지만 Tov와 언어 성능을 일대일로 비교하지는 않습니다. bigint_fib 결과 역시 측정 환경에 따라 승자가 바뀝니다. 이 글은 Tov의 우위 주장을 반박하는 사례를 제시하지만, 고의로 벤치마크를 조작했다는 판단은 코드 차이와 별도로 다뤄야 합니다.

AI 요약

Tov는 TypeScript처럼 작성한 코드를 C로 변환한 뒤 clang으로 컴파일하는 언어입니다. 프로젝트는 일부 벤치마크에서 Rust보다 10배 이상 빠르다고 홍보합니다. Fyra Labs의 글은 bigint_fib, dispatch, map_insert 세 테스트를 재검토하고, Rust 쪽 코드가 불리하게 구성돼 결과가 오해를 부른다고 주장합니다. 컴파일은 Rust의 cargo build --release와 Tov의 기본 최적화 옵션 -O3를 사용했으며, 세 가지 환경에서 측정했습니다.

bigint_fib

30만 번째 피보나치 수를 num-bigint로 계산하는 테스트입니다. Tov 벤치마크 저장소에 실린 수치는 Rust 501ms, Tov 168ms였지만, 글쓴이들은 이를 재현하지 못했습니다. 한 환경에서는 Rust가 Tov보다 1.34배 빨랐고, 다른 환경에서는 Tov가 1.09배 빨랐습니다. 세 번째 환경에서는 Rust가 1.35배 빨랐습니다. 결과가 기기마다 달라졌으며, 적어도 제시된 측정만으로 Tov의 우위를 일반화하기 어렵습니다.

dispatch

도형 1,000개의 넓이를 5만 번 계산합니다. 원래 Rust 코드는 Vec<Box<dyn Shape>>와 트레이트 객체를 써 동적 디스패치를 수행합니다. 글쓴이들은 이를 열거형과 match를 쓰는 코드로 바꾸고 배열을 사용해 다시 측정했습니다. 이 버전은 한 환경에서 Tov보다 1.07배 빨랐고, 다른 환경에서는 Tov가 1.03배 빨랐으며, 세 번째 환경에서는 개선한 Rust가 1.05배 빨랐습니다. 글은 원래 Rust 구현이 C 및 Tov 코드와 공정하게 비교되지 않는다며, Tov 개발자가 일부러 Rust를 느리게 만들었을 것이라고 주장합니다. 이는 벤치마크 코드의 차이를 근거로 한 글쓴이들의 해석입니다.

map_insert

문자열 키 200만 개를 맵에 넣고 다시 조회하는 테스트입니다. 원래 Rust 코드는 기본 해시 함수인 SipHash를 사용하는 std::collections::HashMap을 썼습니다. 글쓴이들은 ahash를 사용하고 문자열 키를 미리 만들어 참조로 조회하는 코드도 측정했습니다. ahash 버전은 원래 Rust 코드보다 3.38~4.04배 빨랐고, Tov보다 1.20~1.96배 빨랐습니다. 다만 두 Rust 버전은 해시 함수뿐 아니라 키 생성과 조회 방식도 달라졌습니다. 따라서 이 결과는 언어만의 성능 차이라기보다 구현 선택의 차이도 함께 반영합니다.

글은 Rust가 빠른 코드를 만들 수 있는 도구를 제공하며, Tov가 C로 변환되는 구조에서 의미 있게 C나 Rust를 앞서기는 어렵다고 결론짓습니다. JavaScript를 네이티브 바이너리로 컴파일하는 다른 프로젝트로 Porffor도 언급합니다.

Lobsters 반응

  • @xnacly — Tov 벤치마크를 처음 봤을 때 결과가 그렇게 나왔다면 제대로 된 벤치마크일 리 없다고 생각했습니다. 사용한 자료형만 봐도 알 수 있었습니다. 누군가 시간을 들여 비교 수치를 내서 다행입니다.
  • @mplant — dispatch 테스트는 이해가 잘 안 됩니다. Tov 코드가 동적 디스패치를 시험하는 것처럼 보입니다. Rust 코드를 바꾸면 벤치마크가 실제로 측정하는 대상도 바뀝니다. "Rust보다 빠르다"는 말은 과장일 수 있지만, 이 특정 사례에서 Tov가 동적 디스패치를 더 빠르게 처리할 가능성은 있습니다.
    • @snej — Tov 최적화기가 동적 디스패치를 열거형 버전처럼 처리하도록 단형화했을 수도 있습니다. 그렇다면 Tov에 유리한 점입니다. 다만 이 예시의 동적 디스패치는 관용적인 Rust 방식이 아니며, 많은 프로그래머는 다시 쓴 테스트처럼 열거형을 선택합니다.
    • @david_chisnall — 손으로 최적화한 버전은 닫힌 세계의 타입만 지원하므로 원래 코드와 의미가 완전히 같지 않습니다. 플러그인을 로드하지 않고 전체 프로그램을 최적화한다면 괜찮습니다. 성능 향상은 실행 중 동적 디스패치를 없앤 데서 끝나지 않고, 이후 최적화가 가능해진 데서 나옵니다. 이 작은 테스트에서는 인라이닝과 공통 부분식 제거가 특히 유리합니다. 원래 의미를 유지한다면 다른 구현이 더 빠를 수도 있습니다.
    • @Wilfred — 링크된 C 코드도 힙 할당과 가상 함수 테이블을 쓰는 것 같으니 Box<dyn Shape>와 비교하는 게 공정해 보입니다. C 컴파일러가 이 코드를 더 잘 최적화하는 걸까요?
    • @aw1621107 — Godbolt에서 빠르게 확인한 C와 Rust의 디스패치 루프는 꽤 비슷합니다. 링크된 폴더의 C와 Rust 버전도 제 기기에서는 성능이 비슷합니다.
  • @wink — Rust를 쓰기 시작한 뒤로 std::collections::HashMap이면 충분해서 ahash를 몰랐습니다. 표준 문서도 중간 크기 키에서는 성능이 경쟁력 있다고 설명합니다. 이걸 "엄청 느리다"고 보기는 어렵지만 벤치마크 결과 자체는 괜찮아 보입니다.
    • @nrposner — 일반적인 용도에서는 표준 맵이 충분히 빠릅니다. 하지만 해시가 병목이거나 맵 삽입만 시험한다면 언어보다 해시 알고리즘을 시험하게 됩니다. Tov와 TypeScript가 어떤 해시를 쓰는지, 보안성을 고려했는지도 원문이 설명했으면 좋겠습니다.
    • @snej — 느린 쪽은 HashMap 자체라기보다 기본 해시 함수인 SipHash입니다. 맵 구현은 최첨단 해시 테이블 알고리즘을 쓰는 것으로 알고 있습니다.
    • @natkr — 사실 AHashMap은 기본 해시 함수만 다른 표준 HashMap입니다.
    • @val — 뜨거운 루프에서 rapidhash::RapidHasher를 쓰는 HashSet<u64>를 여러 곳에 사용합니다. 방문 노드를 추적하는 너비 우선 탐색 같은 작업입니다. contains()가 며칠짜리 스크립트 실행 시간의 30%를 차지합니다.
    • @rtfeldman — 표준 HashMap은 원래 hashbrown의 SwissTable 구현을 바탕으로 했고, Rust 1.36.0에서 채택됐습니다.
  • @yokljo — Rust, C, C++의 목표는 필요할 때 네이티브 성능을 내도록 다양한 도구를 제공하는 데 있습니다. Tov 같은 언어는 흔한 사용 사례를 빠르게 만드는 대신, 그 범위를 벗어나 최적화하려 하면 공식 도구가 부족해질 수 있습니다.

원문: Fyra Labs 블로그 / 번역·요약: Trawling