はじめに
お盆休みに AoT 関係の知見が得られたため記事にしようと思います。
レンタルサーバーで C# AoT が動いた
前回の記事 で C# AoT が動かなかった問題が解消しました。
サーバーと同じ Linux 環境を用意してコンパイルすると動作します。
今回 Rocky Linux という OS に触りました。WSL にインストールして進めようとしたものの ubuntu
と処理系が違って .NET のインストールにかなり手間取りました。Copilot がないとやらなかったんじゃないかなと思います。Copilot は環境構築にすごく力を発揮します。
C# の DB 接続関係が少し遅い問題のボトルネック解消
C# AoT によってアプリの起動時間は C 等のネイティブ系と同じ速度になりました。
一方で DB 速度がイマイチなのが気になります。
結論としては DB 接続に localhost
を指定するとグローバルで探しに行くのが時間がかかる原因でした。IP を直に指定すると解消します。
Lang | Hello World! | DB Access | |
---|---|---|---|
C | 400 ms | - | |
Rust | 400 ms | 400 ms | |
C# AoT | 400 ms | 600 ms | ← 200 ms くらい遅い |
C# JIT | 800 ms | 1000 ms | JIT は少し遅い |
C# AoT 改善版 | 400 ms | 400 ms |
(https ハンドシェクの時間を含まない)
ライブラリの問題かと思い MySqlConnector を見直してみました。
MySqlSharp が良さそうです。しかしながら解説 https://neue.cc/2017/08/07_556.html を見る限り、速度的にはあまり変わらないとあります。
検証の結果 DB 接続に localhost
文字列を指定するとグローバルで探しに行くため、時間がかかるのが原因でした。
var client = new TcpClient(AddressFamily.InterNetwork) {
ReceiveTimeout = 99999,
SendTimeout = 99999
};
if (server == "localhost")
client.Connect(IPAddress.Loopback, port);
else
client.Connect(server, port);
これを修正したところ Rust に比肩する性能が出ました。.NET が遅い、なんてことにならずに済みそうです。やったね。
┃ ┏━┃ ((;;;;゜;;:::::(;;: ∧__,∧ '';:;;;):;:)):).) . ┃┃
━┏┛ ┏━┃ ━(((; ;;:: ;:::;;⊂(`・ω・´) ;:;;;,,))..).)━━┛ ┃┃
━┏┛ ┛ ┃ ((;;;:;;;:,,,." ヽ ⊂ ) ;:;;))):..,),)).:) ┛┛
┛ ┛ ("((;:;;;(;:: (⌒) |. .どどどどど・・・ ┛┛
三 `J
(.NET でパフォーマンスが出て歓喜する様子)
そのうち DB ドライバも書いてみたいですね(少し書き始めている)。
WinForms アプリの AoT
WinForms は AoT ビルドしようとするとコンパイルエラーになりますが、↓で回避できます。
<PropertyGroup>
<_SuppressWinFormsTrimError>true</_SuppressWinFormsTrimError>
</PropertyGroup>
- 出力アプリのサイズは最小構成で 18MB くらいです
- ファイルサイズは大きい気もしますが、.NET の自己完結型出力だと 100MB くらいなのでかなり圧縮されています
- リフレクション系の機能にタッチするとアプリが落ちます
- 具体的には
DataGridView.DataSource
にタッチすると落ちます - その他の型の
DataSource
も同様に落ちます
- 具体的には
- 図らずとも難読化される利点があります
かんたん AoT 出力
https://qiita.com/spc_ksudoh/items/dc579d694da529f0d932 の記事がとても参考になります。
.NET preview6 の新機能で、単一のコンソールアプリ .cs ファイルを Aot 出力できるようになりました。
標準だと出力先は Tmp フォルダになるようですが、出力先を指定することもできます。
dotnet publish Program.cs -o publish
publish
フォルダに Program.exe
が出力されます。
AoT でアセンブリ取得しようとすると落ちる問題
AoT でリフレクションを使ったアセンブリの取得をするとアプリが落ちます。
// 現在のメソッドを呼び出しているアセンブリを取得
Assembly.GetCallingAssembly();
// 実行中のアプリのエントリポイントがあるアセンブリを取得
Assembly.GetEntryAssembly();
// このコードを実行しているアセンブリを取得
Assembly.GetExecutingAssembly();
↑ これらはランタイム版の .NET でも x64 環境で呼び出すとなんか落ちることがあるんですよね・・・
アセンブリ型を使う場面はあまりないですが、アセンブリの埋め込みリソースを取得する
場合に使います。以下のように書くのがよさげです。
using var stream = typeof(MyType).Assembly.GetManifestResourceStream("Namespace.ResourceName.txt");
typeof(T)
演算子は AoT でも安全です。AoT の場合はコンパイル時に型が決定するため、多分 this.GetType()
よりいいでしょう。
.NET10 Preview 7
.NET10 Preview7 が利用可能になりました。
今回は C# 機能追加は特になさそうです。時期的にリリースに向けて安定性向上とかに注力しているのでしょう。
例年のスケジュールですと
- 8月:Preview7
- 9月:RC1
- 10月:RC2
- 11月:.NET 10 正式リリース(LTS)
以前のバージョンのサポート状況はこんな感じです。.NET 8 のサポート期限が意外と短いです。
バージョン | 元のリリース日 | リリースの種類 | サポート終了 |
---|---|---|---|
.NET 9 | 2024年11月12日 | STS | 2026年5月12日 |
.NET 8 | 2023年11月14日 | LTS | 2026年11月10日 |
おわりに
AoT は興味深い分野で、試行錯誤中です。既存のコードは意外とリフレクションを使った部分があり、手を加えないと動かないことが多いです。
DirectX 系のアプリも AoT できることを確認できたので、ゲームアプリのリリースも AoT でできそうな感触を得ました。