6️⃣

【.NET 10.0】 dnx 【Preview 6】

に公開

.NET 10.0 Preview 6 が昨日寝る前にリリースされていました。いくつか気になった点を摘んでみます。

https://x.com/sator_imaging/status/1945290308951929168

dnx & dotnet tool exec

npx (Node.js Package eXecute?) と同じようにパッケージのインストールなしで dotnet tool を実行できるようになりました。

dnx PackageName --yes -- [options...]

--yes 付けないと初回実行時に実行して良いか聞かれる仕様、npx と同じなんだけど要るかなコレ? CI/CD 向けにインストールなしで実行って用途なんでは。。。

今のところ NuGet パッケージの ToolCommandName からパッケージ名を引いてくるという処理は行われないので、先日作った 👇 は static-import っていう別名で NuGet パッケージを作って(!)dnx static-import 出来るようにしてみました。

https://zenn.dev/sator_imaging/articles/7b1df223d17d89

駄文

良い感じなんだけど、今のところ dotnet は npx(npm)ほど普及していないのが悩ましい。Docker イメージにデフォで入ってないことが多いのでどうする? という印象。

標準ライブラリだけでほとんどの事が出来るのが .NET の良いところではあるんだけど、反面 SDK のインストールサイズが大きいってのはちょっとした用途には不向きって感じでしょうか。その点 node_modules が色んな所に溢れかえるけどインストールサイズが小さいのが Node.js の良いところ。(コミコミで100MB程度)

いっそのこと

npx dotnet -- package@x.x.x [options...]

で SDK インストールから実行まで出来るようにしちゃえばいいんじゃないですかね? 既にパッケージ名取られちゃってるけども。

(将来的には各環境向けにコンパイルされた軽量でネイティブコードな実行環境を配布! みたいなパワープレイで全部解決しそうだけど)

ネイティブ AOT

https://github.com/dotnet/core/blob/main/release-notes/10.0/preview/preview6/sdk.md#platform-specific-net-tools

.NETツールは、1つのパッケージで複数のRuntimeIdentifier(RID)をサポートして公開できるようになりました。ツール作成者は、サポートされているすべてのプラットフォーム用のバイナリをバンドルでき、.NET CLIはインストールまたは実行時に正しいものを選択します。これにより、クロスプラットフォームツールの作成と配布がより簡単になります。

thx: DeepL 翻訳

こちら、サンプルで示されている ToolType というオプションが追加された訳ではないという分かりづらさ。

dotnet pack -p ToolType=<variation>  # 汎用的に使えないよ!

サンプルのリポジトリを見ると分かりますが、ハードコードされた <ToolType> というプロパティーを C# プロジェクト側で確認してオプションを変えてるだけ。

https://github.com/baronfel/multi-rid-tool/blob/81e93cec8ba7aacdd1f3933fd594cf55f6b3e186/toolsay/toolsay.csproj#L28-L56

<RuntimeIdentifiers> で複数のターゲットを指定して dotnet pack したら一括で~~とかもない。従来通り -r または --use-current-runtime で個別にパッケージングする必要がある。

サンプルの GitHub アクションには以下の形式でパッケージングされた .zip がアーティファクトとして上がっているけど、

  • all-aot-packages.zip
    • aot-package-agnostic/
      • <pacakge>.<version>.nupkg
    • aot-package-ARCH/
      • <pacakge>.<ARCH>.<version>.nupkg

この構成だと dnx toolsay で良い感じに AOT ランタイムを実行してくれるのかは不明。肝心の NuGet にパッケージがアップされていないので試せず。

static-import で試そうと思ったけどネイティブ AOT コンパイルがリンカーでコケて断念。

ともあれ dotnet pack

  • 良い感じにすべてのネイティブ AOT ビルドを一つの .nupkg(ZIPアーカイブ)にまとめてくれる

という機能は実装されていない。要するに dotnet pack は何も変わっていない。多分 dotnet tool exec 側が .nupkg 内のファイルの中から良い感じにベストなモノを選ぶようになったって話でしょう。

最終的に一つの .nupkg(.zip)に全部のランタイムを纏めないとダメだと思うので dotnet pack のアップデート待ちでしょうか。ライブラリ依存無しで実行できる .nupkg は配布サイズがヤバいのでうーーん? どこで使う? dnx だときつくね? って感じもありますが。

while ループの最適化

コレは Unity 等の古い環境でも(手動で)適用可能な最適化。

https://x.com/sator_imaging/status/1945302891813589128

コンパイル結果としては以下の通り。(Unity 上で確認)

while(従来)

  • 無条件ジャンプで while の条件判定に飛ぶ
  • while ブロック内のコード群
  • while の継続条件チェック(最初の無条件ジャンプで飛んでくる場所)
  • while を抜けた後のコード群

if...do...while(最適化)

  • while と同じ条件でチェックして false ならループを抜けた後にジャンプ
  • do...while ブロック内のコード群
  • do...while の継続条件チェック
  • do...while を抜けた後のコード群(最初の条件分岐で飛んでくる場所)

従来方式だと while ブロック内のコード群を一回飛び越えてからまた戻る、ってコードパスを通る確率が高かったものがコードサイズを1バイト増やすことで改善されたって感じでしょうか。正直何が変わるの? ブロック内のコードサイズ次第? って感じですが BCL 全体に適用されると考えればデカそうです。

c/c++ 等の while (true) より do...while(true) の方が良いよ、っていうコンパイラーが最適化されている現在では通用しない最適化が C# では未だ有効だった、的な話っぽい?

古い環境でも

👇 と同じでチューニングしまくれば最新の .NET 環境と同等の性能を引き出すことができるので、一応は覚えておきたいテクニックですね。(List のベンチマーク結果を参照のこと)

https://qiita.com/sator_imaging/items/c4ac0dab548a3e78878f#ベンチマーク

※ デカい SDK じゃなきゃやっても何も変わんないと思いますがw

おわりに

単一ファイルで実行できるアプリは夢が広がるけど npx で良いかなー感があります。.NET SDK インストールされてないですからね。あと2バージョン後ぐらいですかね。

以上です。お疲れ様でした。

Discussion