ふとしたことから始めたNET ランタイムへのコントリビュート

>100 Views

September 19, 26

スライド概要

2026年9月19日に開催されたC# Kaigi 2026の登壇資料になります。

profile-image

プログラミングが好きな一般C#er。

シェア

またはPlayer版

埋め込む »CMSなどでJSが使えない場合

(ダウンロード不可)

関連スライド

各ページのテキスト
1.

ふとしたことから始めた .NET ランタイムへのコントリビュート C# Kaigi 2026 2026/09/19 (土) prozolic

2.

自己紹介 prozolic(プロゾリック) 業務アプリケーションエンジニア/一般C#er GitHub: @prozolic 最近の趣味: ウォーキング 2

3.

内容 .NET ランタイムへのコントリビュートを始めたきっかけ コントリビュートする上で、どこからはじめたか、どんなことをしたか 作成した/使用したツールについて ※個人的な目線での内容も含まれています。 3

4.

コントリビュートするきっかけ 自作のOSS開発以外に、大規模のOSS開発へ貢献に興味があった。 じんぐるさん(@xin9le)の.NET 9でのパフォーマンス改善の貢献が書かれたブログ [1] を読んで、私もやってみようと決意! この時に.NET ランタイムが、オープンソースであったことを再認識 [1] https://www.sigmact.com/updated/dotnet/net9-perf-pr/ 4

5.

.NET ランタイム dotnet/runtimeは、大きく分けて、三つのカテゴリに分類される。 私は、Libraries と CoreLib(C#) で貢献を開始しました。 Libraries メインライブラリ CoreLib 低レベルコアライブラリ(組み込み型など) Runtime 実行エンジン(JITコンパイラ/GCなど) C# C#、C/C++ C/C++ 5

6.

何から始めたらいいのか? 6

7.

とりあえず、環境準備 ビルド/デバッグ環境をまず準備するところから始めました。 手順は、dotnet/runtimeのWorkflow Guideに基本的に全て書いてあります。 だけど失敗するので、これがある意味一番大変。 失敗例 ビルドがたまに失敗する デバッグができない 主にキャッシュが原因。今もバージョン更新時に発生 デバッグビルドを忘れていた 環境固有の問題が出る Windows + 日本語環境で失敗することがあった 7

8.

貢献方法 プルリクエスト 新規機能追加 パフォーマンス改善 まずは、ここから始める 不具合修正 typo 修正 バグレポート報告 API提案 8

9.

私が行った進め方 最初は見る領域を狭くする。 自分が使ったことのあるものから見る。 過去使われた既知パターンを調べて、適用できるか確認する。 過去のマージされたパフォーマンス関連のPRを読む。 見る領域を徐々に広げていく。 9

10.

どこから始めるか そもそも当たり前ですが、領域自体が多い。 とりあえず、 System.Collections.Immutable を選びました。 理由は、当時OSSの実装で ImmutableArray を使っていたからという軽い理由 これでもまだライブラリの一部 10

11.

パフォーマンス改善 パフォーマンス改善(高速化、アロケーション削減など)自体は、現在進行形でどの領域でも 活発に行われている。 コンパイラレベルで対応する。 既存のアルゴリズム実装を別の実装に切り換える。 新しいAPIに切り換える。 過去に行われた手法を別の場所で使う。 まずは過去のPRからどんなことをしているのか確認から始める。 並行してソースコードも読みつつ。 11

12.
[beta]
既知の最適化パターンを使う
ということで見つけたのが、 ImmutableArrayExtensions.SequenceEqual の高速化
最適化パスの追加(コレクションやLINQなどで昔から使われている方法)
public static bool SequenceEqual<TDerived, TBase>(
this ImmutableArray<TBase> immutableArray,
IEnumerable<TDerived> items,
IEqualityComparer<TBase>? comparer = null)
{

ICollection<T>であれば、LINQを
if (items is ICollection<TBase> itemsCol)
{
実行する
immutableArray.ThrowNullRefIfNotInitialized();
return Enumerable.SequenceEqual(immutableArray.array, itemsCol, comparer);
}
return Enumerate(immutableArray, items, comparer);
もともとはforループで一つ一つ判定

}
12

13.

既知の最適化パターンを使う ベンチマーク これだけでも高速化する 最適化パス以外もほぼ変わらない 13

14.

レビュー過程で実装変更 一番最初の提案した実装は、 ImmutableArray の内部で使われている別のロジックを使用 レビューワから「 Enumerable.SequenceEqual を使うのが最速」とアドバイス ベンチマークから Enumerable.SequenceEqual + 既存実装の形で落ち着く 途中で dotnet/performance にベンチマーク追加PRも行うサブイベントも発生 14

15.

色々なケースでベンチマークを計測 ベンチマーク結果が無いとどれくらい良くなったのか分からない。 また良く使われるケースで回帰していないか確認できるようにする必要がある。 大容量だと速くなるが、小規模だと遅くなるなどは、APIによってはだめ。 私も結構これと同じことをして、PRしなかったものもありました。 そのため、パフォーマンス改善では、ベンチマーク結果とベンチマークで使ったソースも載せ ることが大事(当たり前) 15

16.

別の部分でも使えないか確認 あとは別の部分でも同じような形が使えないかソースコードをチェックする。 実際に、 string.Concat でも最適化パスを追加するなど行いました。 ただし、最適化パスを追加すると分岐処理が追加され、わずかにオーバーヘッドも入るため、 なんでもかんでも入れると遅くなる。 オーバーヘッドとのトレードオフに注意する必要がある。 16

17.

不具合の見つけ方(一例) 前提として、基本的な使い方でバグが起きることはあまりないです。 私個人としては、ほとんど遭遇しない(稀に遭遇する) System.Collections.Immutable では、異常値を入力するとバグが発生しそうなコードがあ りました。 そこで色々なケースを入力してみることに。 17

18.

バグ発見 結構見つかりました... 18

19.

オーバーフローによる上限/下限チェックのすり抜け List<T> などの主要コレクションは行われているチェックとは異なっていることが原因。 一部ケースはすでにバグ報告されていたが、まだ未対応だった。 修正前 修正後(uintキャストの追加) Requires.Range( count >= 0 && startIndex + count <= this.Count, nameof(count)); Requires.Range( count >= 0 && (uint)(startIndex + count) <= (uint)this.Count, nameof(count)) 19

20.
[beta]
その他にもコード最適化も行ったり
throw 句をヘルパーメソッドに変更する、いわゆるインライン化しやすくなる対応。
throw 句が存在するとコンパイラがインライン対象外と判定される。

同様の対応が過去にも複数で行われていたので、これも既知のパターン。
変更前

変更後

public static T First<T>(
this ImmutableArray<T>.Builder builder)
{
Requires.NotNull(builder, nameof(builder));

public static T First<T>(
this ImmutableArray<T>.Builder builder)
{
Requires.NotNull(builder, nameof(builder));

}

if (!builder.Any())
{
throw new InvalidOperationException();
}

if (!builder.Any())
{
ThrowHelper.ThrowInvalidOperationException();
}

return builder[0];

return builder[0];
}

20

21.

継続して進めるために 他の領域でも適用できないか、同じバグがないかという視点で、他の領域のソースコードを 読む。 新しい領域の構造や内部ロジックなどの知識を獲得できる。 string.IndexOf ( StringComparison.OrdinalIgnoreCase )のようなコア部分でも、同 じようなバグを見つけることもありました。 たまに自作のOSSへ反映するなどメリットがある。 21

22.

そして深く把握すること ただし、さらに深淵を知るにはもっと知識が必要。 領域ごとに使う知識も必要になり、複雑な動きやソースコードだけでよくわからない部 分もたくさんある。 ひたすらデバッグしたり、AI エージェントツールを使って理解を深めることが大事。 22

23.

最新の状況を どうやって追いかけるのか? 23

24.

作ったツール PR Digest.NET マージされた最新のプルリクエストの概要をAIで日本語要約するサイト 英語が苦手で、PRの内容を把握しやすくするために作成 24 https://prozolic.github.io/PRDigest.NET

25.

作ったツール PR Digest.NET 指定のラベルが適用されているプルリクエストなども確認できる。 どの領域がよくマージされているかなどを把握できる。 25

26.

よく使うツール/環境変数 ILSpy 最新機能で生成されるコードや最新SDKでビルドしたコードを確認する際に使用。 DOTNET_JitDisasm 環境変数 プログラムの逆アセンブラ(disassembler) メソッド単位がどのように展開されるなど、パフォーマンス分析時に使う。 最近はAIエージェント経由で、出力結果を確認することが多い。 26

27.

まとめ ふとしたことで始めた .NET ランタイムの貢献は色々大変。 それでも少しずつ進めていくことが大事。 他のPRの内容についても、ツールなど用いて日々把握していくことが大事。 .NET ランタイムは面白い。 27