Roc 대 C: Apple Silicon에서 roc-deflate 벤치마크가 libdeflate의 2.6% 이내
순수 함수형 언어 Roc로 완전히 작성된 DEFLATE 압축 패키지가 2021년 M1 MacBook Pro에서 SIMD 최적화된 C 라이브러리 libdeflate보다 중앙값 기준 2.6% 느렸고, Ubuntu를 실행하는 AMD Ryzen 9 3900X에서는 8.9% 느렸습니다. Roc 창시자 Richard Feldman은 2026년 10월 22일에 벤치마크를 게시했으며, 그 결과를 그가 "예상하지 못한" 것이라고 설명했습니다: 자동 메모리 관리, 경계 검사, 안전하지 않은 탈출구가 없는 함수형 언어가 다른 어떤 언어도 따라잡지 못한 수작업으로 조정된 C 구현에 한 자릿수 퍼센트 차이로 접근한 것입니다.
한 문단으로 보는 벤치마크
Niclas Ahden이 작성하고 유료 프로덕션 작업에 사용된 DEFLATE 패키지인 roc-deflate는 Silesia 압축 코퍼스에서 libdeflate와 벤치마킹되었습니다. 두 구현은 동일한 출력을 생성하는 것으로 검증되었으며, 압축 레벨 L1부터 L12까지 144개의 벤치마크 케이스가 실행되었습니다. roc-deflate가 아직 스트리밍을 구현하지 않았기 때문에 비스트리밍 API만 비교되었습니다. 보고된 수치는 M1 MacBook Pro에서 15회 실행, Ryzen 3900X에서 7회 실행의 중앙값입니다. 주요 수치: roc-deflate는 M1에서 중앙값 기준 2.6% 느렸고, Ryzen 3900X에서는 8.9% 느렸습니다.
Roc이 C의 libdeflate보다 빠른가요, 느린가요?
Roc은 평균적으로 느리지만, 그 격차는 작으며 일부 입력에서는 roc-deflate가 이깁니다. 전체 144개 케이스 스위트에서 중앙값 둔화는 Apple Silicon에서 2.6%, x64에서 8.9%였습니다. 기사는 최악의 입력에서 roc-deflate가 libdeflate보다 18% 느렸고, 최상의 입력에서는 18% 이상 빨랐다고 보고합니다. 이러한 승리는 C가 접근할 수 없는 최적화의 산물이 아닙니다; 두 구현이 단순히 다르기 때문에 특정 입력이 다르게 배치됩니다. libdeflate에서 roc-deflate로 전환하면 일부 입력에서 속도 향상을 얻을 수 있지만 평균적으로는 아닙니다.
M1 머신에서 압축 레벨 L1, 15회 실행 중앙값의 대표 수치:
dickens파일 압축: libdeflate 230.57 MB/s, roc-deflate 202.92 MB/s.nci파일 압축: libdeflate 601.03 MB/s, roc-deflate 529.49 MB/s.sao파일 압축: libdeflate 160.21 MB/s, roc-deflate 145.28 MB/s.
Ryzen 3900X에서 L1, 7회 실행 중앙값의 대표 수치:
dickens압축: libdeflate 191.10 MB/s, roc-deflate 182.30 MB/s.nci압축: libdeflate 387.30 MB/s, roc-deflate 428.60 MB/s.sao압축: libdeflate 157.40 MB/s, roc-deflate 138.50 MB/s.
순수 함수형 언어는 성능 패널티를 지불하나요?
Roc은 C보다 낮은 성능 상한선을 지불하지만, 컴파일러가 불필요하다고 증명할 수 있는 검사를 제거하기 때문에 패널티는 예상보다 작습니다. Roc 코드는 완전히 메모리 안전하며, 자동 참조 카운팅을 사용하고, 동적 크기 컬렉션에 대한 경계 검사를 수행하며, 이 중 어떤 것도 선택 해제할 수 있는 Rust 스타일의 unsafe 키워드를 제공하지 않습니다. 기사는 C의 성능 상한선이 Roc보다 높다고 명확히 밝힙니다: roc-deflate가 현재 libdeflate를 이기는 모든 입력에 대해 C 코드가 그 격차를 좁힐 수 있지만, 그 반대는 사실이 아닙니다. roc-deflate가 C 성능에 접근하는 것은 Roc의 최적화 파이프라인이 많은 경계 검사와 참조 카운팅 작업이 제거해도 안전하다고 증명했기 때문입니다.
Roc이 튜닝된 C 라이브러리에 이렇게 가깝게 측정된 것은 이번이 처음입니다. 기사는 하나의 결과가 일반화되지 않는다고 명시합니다: 저자가 아는 한, 이것은 어떤 Roc 패키지에 투입된 최대의 최적화 노력이므로, 이 접근 방식이 다른 코드베이스에 얼마나 잘 전이되는지는 완전히 미지수입니다.
남은 한 자릿수 격차가 사는 것
기사는 순수 Roc 코드가 제공하고 C, Zig, Rust 코드가 제공할 수 없는 네 가지 보장을 나열함으로써 트레이드오프가 가치 있다고 주장합니다. 이 중 어떤 것도 언어에서 선택 해제할 수 없습니다.
메모리 안전
roc-deflate는 Node.js의 2026년 10월 보안 릴리스에서 공개된 Brotli 원격 코드 실행 취약점(CVE-2026-111497)을 일으킨 종류의 검사되지 않은 메모리 작업에 접근할 수 없습니다. 순수 Roc로 작성된 패키지는 구현의 어떤 수준에서도 경계를 벗어난 읽기 또는 쓰기를 할 수 없습니다. 언어가 이를 허용하는 기본 요소를 제공하지 않기 때문입니다.
부작용 없음
Roc의 타입 시스템은 어떤 함수가 순수하고 어떤 함수가 부작용을 수행하는지 추론합니다. 순수 함수는 Bool -> Bool과 같이 얇은 화살표로 주석이 달리고, 효과가 있는 함수는 Bool => Bool과 같이 두꺼운 화살표를 사용합니다. roc-deflate의 공개 주석이 얇은 화살표만 사용하기 때문에 컴파일러는 전체 패키지가 순수 함수로 구성되어 있음을 검증합니다.
공급망 보안
Roc에는 임의의 외부 함수 인터페이스가 없으므로 순수 Roc 패키지는 I/O를 시도하거나 임의의 메모리에 접근할 수 없음이 증명 가능합니다. 기사는 손상된 순수 Roc 패키지를 통해 악의적인 행위자가 달성할 수 있는 최악의 결과는 잘못된 값을 반환하거나, 충돌하거나, 시스템 리소스를 낭비하는 것이라고 주장합니다. Roc 패키지는 또한 빌드 스크립트를 지원하지 않으며, 이는 2026년 8월 Rust arrayref 패키지 손상에 사용된 메커니즘입니다.
이식성 및 결정론
Roc 순수 함수는 코드가 컴파일되는 모든 대상에서 동일한 인수에 대해 동일한 값을 반환하도록 설계되었으며, 동일한 머신에서만이 아닙니다. 충돌하거나 종료되지 않을 수 있지만, 그 동작도 대상 간에 결정론적일 것으로 예상됩니다. 유일한 예상되는 교차 시스템 차이는 실행 중간에 한 시스템이 메모리가 부족해지는 것입니다.
기사는 선택을 반대로 제시합니다: 보장된 메모리 안전, 보장된 부작용 없음, 보장된 교차 대상 결정론, 그리고 악용 가능한 빌드 스크립트가 없다는 보안 이점을 포기하고 평균 한 자릿수 속도 향상을 얻겠습니까? 답은 사용 사례에 따라 합법적으로 다르다고 언급합니다. Roc 자체 컴파일러는 최대 하드웨어 성능을 원하기 때문입니다.
격차를 줄인 Roc 특화 최적화
대부분의 이득은 일반적인 최적화 기술에서 나왔으며, 기사는 저자가 비용을 지불하고 배운 Casey Muratori의 Performance-Aware Programming Series를 추천합니다. 네 가지 기술은 Roc 특화입니다.
불필요한 공유 피하기
Roc의 가장 효과적인 최적화는 기회주의적 변형(opportunistic mutation)으로, Koka는 이를 "Functional but In-Place"(FBIP)라고 부릅니다. 성능 관점에서 중요한 규칙은 불필요한 공유를 피하여 값이 고유하게 유지되고 따라서 제자리 변형에 적합하도록 하는 것입니다.
루프 밖으로 경계 검사 끌어올리기
Roc은 프로그래머가 경계 검사를 건너뛰는 것을 허용하지 않지만, 컴파일러는 통과할 것임을 증명할 수 있을 때 삭제할 수 있습니다. 경계 검사는 루프 내부에서 가장 비용이 많이 들기 때문에, 루프 전에 명시적 검사를 추가하면(예: 컬렉션 길이가 사용된 최고 인덱스보다 작거나 같을 때 조기 반환) 최적화 프로그램이 루프 내부의 더 비싼 검사를 안전하게 제거할 수 있습니다.
컴파일 타임에 조회 테이블 구축
Roc의 컴파일 타임 평가는 런타임 입력에 의존하는 것이 없을 때 컴파일 타임에 조회 테이블을 조립할 수 있습니다. 길이가 컴파일 타임에 알려지기 때문에 Ok(entry) = lookup_table.get(5)와 같은 코드는 컴파일러가 유효하다고 아는 완전한 패턴 매칭이 되어, 비상수 테이블이 필요로 하는 런타임 경계 검사를 피합니다.
래핑 산술 고려
Roc에서 a + b는 a.plus(b)로 디슈가됩니다. U64의 경우 U64.plus는 오버플로 검사를 포함하지만 U64.plus_wrap은 포함하지 않습니다. 컴파일러가 오버플로가 발생하지 않음을 증명할 수 있을 때 둘 다 동일한 명령어로 컴파일됩니다; 증명할 수 없을 때 plus_wrap을 선택하면 더 안전한 기본값을 희생하는 대신 더 빠를 수 있습니다.
배경: Lean 4의 lean-zip이 miniz_oxide를 이겼지만 libdeflate는 못 이김
벤치마크는 "Why Lean is Faster Than Rust"라는 제목의 이전 글에서 동기가 부여되었으며, Lean 4 함수형 프로그래밍 언어 및 증명 보조 도구로 작성된 DEFLATE 라이브러리 lean-zip이 Rust 패키지 miniz_oxide를 능가했다고 보고했습니다. 기사는 그 결과를 직접 인용했습니다:
lean-zip은miniz_oxide가 제공하는 모든 압축 레벨을 이깁니다. 즉, 더 높은 처리량으로 동일한 압축을 제공할 수 있습니다. 가장 빠른 설정에서 15% 더 빠르고 출력은 8.8% 더 작습니다.
같은 글은 lean-zip이 libdeflate를 따라잡을 수 없다고 인정했습니다:
lean-zip은 여기서 최고가 아닙니다:libdeflate는 당연히 그것을 압도합니다(당연한 이유는 아키텍처별 SIMD를 사용하는 매우 세심하게 튜닝된 구현이기 때문이며, Lean에서는 건드릴 수 없습니다).
Roc은 정리 증명기가 아니고 런타임 성능을 최우선으로 하는 함수형 언어로 등장했습니다: 태그라인은 "Fast, Friendly, Functional"이며, 공식 성능 페이지는 빠른 컴파일이 빠른 실행만큼 중요하다고 주장합니다. Roc은 성숙하지 않았습니다; 작성 시점에 첫 번째 번호 릴리스 0.1.0까지 몇 주 남았으며, 패키지 생태계에는 거의 최적화 작업이 이루어지지 않았습니다.
Hacker News 댓글러들이 논쟁한 내용
가장 실질적인 도전은 처리량보다 압축 비율에 관한 것이었습니다. NohatCoder는 다음과 같이 썼습니다:
음, 프로그램이 주어진 레벨에서 압축하는 데 libdeflate만큼 거의 빠릅니다. 하지만 게시물은 주어진 레벨이 libdeflate의 해당 레벨만큼 잘 압축된다는 것을 확립하려는 시도를 전혀 하지 않습니다. 링크된 게시물에는 libdeflate가 동일한 레벨에서 원래 lean-zip보다 훨씬 더 잘 압축한다는 그래프가 있습니다. 압축 레벨에 대한 데이터 없이는 유용한 결론을 내리기 어렵습니다.
또 다른 댓글러는 Lean 비교 기준이 너무 약하다고 주장했습니다. VorpalWay는 다음과 같이 썼습니다:
Miniz_oxide는 의도적으로 단순한 구현입니다. 더 새로운 zlib-rs와 비교하는 것이 더 공정할 것입니다. zlib-rs는 zlib-ng(C에서 가장 빠른 구현)도 이깁니다.
VorpalWay는 zlib-rs 크레이트와 Trifecta Tech Foundation의 글 "zlib-rs is faster than C"를 Rust가 이미 가장 빠른 C 구현과 일치하거나 이긴다는 증거로 링크했으며, 이는 Roc 결과의 중요성을 바꿀 것입니다.
jeffbee는 libdeflate가 의미 있는 성능 참조라는 전제에 의문을 제기했습니다:
이것은 질문을 제기하는 것 같습니다: libdeflate가 빠른가요? libdeflate에 대한 나의 전반적인 인상은 zstd와 비슷한 출력 크기를 생성하지만 40배 더 오래 걸린다는 것입니다. 물론 이들은 다른 알고리즘이라는 것을 알고 있지만, 그렇게 다르지 않습니다. 둘 다 LZ 계열의 Huffman 라인에 있습니다. 그렇다면 왜 libdeflate가 성능 벤치마크로 간주됩니까?
jandrewrogers는 함수형 언어가 성능에 중요한 작업에서 뒤처지는 경향이 있는 구조적 설명을 제공했습니다:
최적화 가능성의 많은 차이는 C/C++가 상당히 극단적인 복사 생략을 허용한다는 것입니다. 메모리 대역폭이 주요 성능 병목이 되는 경향이 있기 때문에 이것은 중요합니다. C/C++ 코드는 원격 상태의 일부에 쉽게 도달하여 직접 변형할 수 있습니다. 컴파일러는 접근 방법이나 패턴이 아무리 추해도 거의 반대하지 않습니다. 불변식을 유지하는 것은 프로그래머의 몫이며 컴파일러가 항상 그것을 꿰뚫어 볼 수는 없습니다. 이러한 생략된 복사는 누적되지만, 일반적으로 유지보수성과 안전성 비용이 듭니다. 이것은 함수형 언어가 이론적으로 대규모 병렬 처리에 좋은 특성을 가지고 있음에도 불구하고 실제로 슈퍼컴퓨팅에 평범한 경향이 있는 이유와 관련이 있습니다.
andai는 Roc을 미성숙하다고 설명한 기사의 프레임과 컨설턴트가 수년 동안 프로덕션에서 사용했다는 주장 사이의 불일치를 제기했습니다:
그런 다음 컨설턴트가 프로덕션에서 사용하는 것을 언급하는 비디오를 링크합니다(이미 몇 년 동안?), 저를 혼란스럽게 했습니다. 미성숙한 기술을 클라이언트 작업에 사용하는 것을 어떻게 정당화할 수 있습니까? 아니면 그것이 고장나서 고쳐야 할 때 추가 이점입니까?
johnny22는 라이브러리의 다운로드 수가 성능의 증거라는 프레임에 반대했습니다:
"X번 다운로드된 크레이트를 이긴다"고 말하는 것은 그것이 인기가 있다는 것 외에는 아무것도 말하지 않습니다. 거기에 약간 더 자세한 내용이 있으면 좋겠습니다.
ReDress는 성능에 중요한 시스템에서 한 자릿수 격차는 허용되지 않는다고 주장했습니다:
또한 성능이 많은 경우에 궁극적인 관심사라는 점을 지적하고 싶습니다. 분당 특정 메서드에 1천만 요청을 하는 서버가 있다면 어떤 성능 타협도 감당할 수 없습니다. 어떤 것도, 전혀. Rust는 고성능 컴퓨팅과 관련하여 C/C++를 결코 대체하지 않을 것입니다.
gobdovan은 Roc 특화 최적화가 컴파일러 검사 패스의 라이브러리로 노출될 수 있는지 물었고, JavaScript에서 비슷한 경험을 보고했습니다: 충분한 노력으로 V8에서 최적화된 C 코드의 약 10% 이내에 도달할 수 있지만, SIMD를 제외하고는 gobdovan이 V8이 명백한 경우조차 최적화하지 않는 것 같다고 말했습니다.
다른 댓글들은 인접한 점을 제기했습니다. WalterBright는 D에 컴파일러가 검사하는 pure 함수 주석이 있다고 언급했습니다. cubefox는 Haskell이 루프가 있는 언어와 달리 효율적인 스택 사용을 위해 주의 깊은 꼬리 재귀를 요구한다고 언급했습니다. giovannibonetti는 Roc이 결국 고수준 SIMD 추상화를 얻을 수 있다고 추측하며 데이터 병렬 함수형 언어 Futhark를 지적했습니다. amelius는 장기적 이상은 FP 코드를 작성하는 동안 AI가 정확성 보존 변환을 사용하여 더 빠른 코드로 번역하는 것이라고 제안했습니다. deadbabe는 강력한 LLM이 C 이상을 사용할 이유를 제거하는지 물었습니다. michaelteter는 Roc의 함수형 컴파일과 Ruby 같은 인체공학의 조합을 좋아했습니다. jdw64는 어떤 잘 알려진 프로그램이 Roc으로 구축되었는지와 구문이 얼마나 어려운지 물었습니다.
다음 작업 방향
Feldman은 roc-deflate에서 쉬운 성과는 이미 얻었다고 믿지만, roc-deflate(x64 CPU에서 특히)와 Roc의 컴파일러 최적화 파이프라인 모두에서 남은 기회를 보고 있습니다. 그는 Linux에서 벤치마크 스위트를 설정하고 실행하는 데 사용된 스크립트를 게시했으며, 이 스크립트가 Roc 컴파일러 개정판과 roc-deflate 버전을 모두 고정한다고 언급하고, 스크립트가 Claude Opus 5.5에 의해 생성되었고 보안 검증을 받지 않았으므로 샌드박스 외부에서 실행자의 책임 하에 실행해야 한다고 경고했습니다.
Feldman은 자신의 부분에서 다가오는 Roc 0.1.0 릴리스에 집중할 것이라고 말했으며, 이를 8년 만에 이루어지는 것이라고 설명했습니다.
Sources
관련
- Dispatch
- Dispatch
- Dispatch
- Dispatch
- Dispatch