Roc vs C: roc-deflate ベンチマークがApple Siliconで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とベンチマーク比較されました。2つの実装は同一の出力を生成することが検証され、圧縮レベル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がアクセスできない最適化の産物ではありません。2つの実装は単に異なるため、特定の入力は異なる結果になります。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ライブラリにこれほど近いと測定されたのはこれが初めてです。記事は、1つの結果が一般化されないことを明確にしています。著者が知る限り、これはこれまでにRocパッケージに投入された中で最も最適化努力がなされたものであり、そのアプローチが他のコードベースにどの程度移転するかは完全に未知数です。
残りの一桁台のギャップが買うもの
記事は、純粋なRocコードが提供し、C、Zig、およびRustコードが提供できない4つの保証を列挙することで、トレードオフが価値があると主張しています。これらは言語でオプトアウトできません。
メモリ安全性
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を著者が費用を支払って学んだ情報源として推奨しています。4つの技術がRoc固有です。
不要な共有を避ける
Rocの最も効果的な最適化は日和見的ミューテーションであり、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-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分間に1000万リクエストを特定のメソッドに送信するサーバーがある場合、パフォーマンスの妥協は許容できません。まったく、まったくです。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