開発者のMartin Finkelは、VLCメディアプレーヤーの特定のビデオフィルタであるガウスぼかしを移植することで、CとC#のパフォーマンス比較を行いました。この実験の目的は、VLCのネイティブ言語であるCと比較して、C#のマネージドな性質やガベージコレクタがパフォーマンスを大幅に損なうかどうかを判断することでした。公平な比較を確実にするため、Finkelは元のCの実装と同じループ、整数演算、および生ポインタを使用した、行単位のC#移植を作成しました。C#バージョンは.NET Native AOTを使用してコンパイルされ、VLC内でネイティブDLLプラグインとして実行できるようにしました。

元のフレームと、異なるシグマ値でぼかされた同じフレームの並列比較。
シグマ値2、4、8で適用されたガウスぼかしフィルタの視覚的な例。martinfinkel.com

開発者のMartin Finkelは、VLCメディアプレーヤーの特定のビデオフィルタであるガウスぼかしを移植することで、CとC#のパフォーマンス比較を行いました。

初期の結果では、明確なパフォーマンスの差が現れました。C#の移植は、元のCコードよりも1フレームあたり約23%長くかかりました。マシンコードの分析により、.NET RyuJITコンパイラがClangコンパイラよりも多くの命令を生成していることが判明しました。具体的には、ループ境界の再計算や、レジスタではなくメモリ内に合計値を保持する動作などが挙げられます。しかし、ループ境界をローカル変数に持ち上げ、32ビットから64ビットへの変換を避けるためにインデックスに`nint`を使用するという2つの小さな調整を加えたことで、C#移植のパフォーマンスはCバージョンの8%以内まで改善されました。これは、パフォーマンスの差の多くが言語固有の制限ではなく、コンパイラが特定のC#構文をどのように処理するかに起因していることを示しました。

C#の移植と現在のVLC実装のフレームレートを示すパフォーマンス比較チャート。
C#の移植が6.4 FPS、元のVLC実装が7.9 FPSで動作している初期のパフォーマンス結果。martinfinkel.com

初期の結果では、C#の移植が元のCコードよりも1フレームあたり約23%長くかかるという明確なパフォーマンスの差が見られました。

その後の最適化のラウンドでは、除算を乗算に置き換える、メモリアクセスパターンを最適化する、ガウスのベル曲線の対称性を利用するなど、アルゴリズムの洗練を用いて両方のバージョンを改善しました。最終的な「調整済み」のラウンドでは、両方のバージョンでAVX2 SIMD命令を利用して大幅な高速化を実現し、C#バージョンはCとほぼ同等の性能を発揮しました。Finkelは、最終的な10%のパフォーマンスを追求する上ではCが依然として優れた選択肢であるものの、開発者が.NETコンパイラがコードを最適化する特定の仕組みを考慮すれば、C#はCPU負荷の高いVLCプラグインにとって非常に実行可能で生産性の高い選択肢であると結論付けました。この実験は、第11世代Intel Core i7プロセッサを使用し、1080pのクリップを用いて行われました。

最適化されたCおよびC#バージョンと元のVLC実装を比較するパフォーマンス比較チャート。
最初の最適化ラウンド後のパフォーマンス結果。CとC#の両方のバージョンで大幅な高速化が見られます。martinfinkel.com

その後の最適化のラウンドでは、除算を乗算に置き換えるなどのアルゴリズムの洗練を用いて両方のバージョンを改善しました。

最終的に調整されたCおよびC#バージョンがほぼ同等の性能を達成していることを示すパフォーマンス比較チャート。
AVX2 SIMD命令を使用してC#バージョンがCバージョンとほぼ同等の性能を発揮している最終的な調整結果。martinfinkel.com

最終的な「調整済み」のラウンドでは、両方のバージョンでAVX2 SIMD命令を利用して大幅な高速化を実現し、C#バージョンはCとほぼ同等の性能を発揮しました。