rksoftware

Visual Studio とか C# とかが好きです

ReadyToRun 設定

世間で今一番の話題と言えば、.NET Core 3.0 で間違いないでしょう。

そんな .NET Core 3.0 の新機能の中でも、ReadyToRun が気になっている方も多いと思います。もちろん私もその一人です。

■ 何がうれしいの?

事前に動作環境に合わせて最適化されたアセンブリを作ることで、アプリの起動時間を短くできます。環境というのは、Linux x64 や Windows x64 といった粒度です。
エンタープライズ分野の一品ものやデスクトップアプリでは、ターゲットの環境が固定であることが一般的だと思うので設定しない意味はないのではないかと思います。

■ 設定方法

設定方法は、前述のページに記載があり、 .csproj に設定を追加します。

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>netcoreapp3.0</TargetFramework>
    <PublishReadyToRun>true</PublishReadyToRun>
  </PropertyGroup>
</Project>

コマンドラインで発行する場合は発行コマンドのパラメータ /p:PublishReadyToRun=true でも行けるようです。

dotnet publish -r win10-x64 -c Release /p:PublishReadyToRun=true  

■ 試してみる

今回動かしてみた環境は、mac 上の VM の Windows なので、実測時間は何の役にも立たない数値だと思います。
しかし、ReadyToRun とそうでない実行ファイルの比較をしているので、効果の有る/無しについては信じていいかなと思っています。

ReadyToRun 設定をしている実行ファイルのコード

class Program
{
    static void Main(string[] args)
    {
        Console.WriteLine(DateTime.Now.Ticks);
    }
}

ReadyToRun 設定をしていない実行ファイルのコード(全く同じです)

class Program
{
    static void Main(string[] args)
    {
        Console.WriteLine(DateTime.Now.Ticks);
    }
}

それらを、100 回実行してそれぞれの平均をとるコード

    class Program
    {
        static void Main(string[] args)
        {
            var readyProcess = new Process() { StartInfo = new ProcessStartInfo("Ready") { RedirectStandardOutput = true } };
            var notReadyProcess = new Process() { StartInfo = new ProcessStartInfo("NotReady") { RedirectStandardOutput = true } };

            Console.WriteLine(DoProcess(readyProcess));
            Console.WriteLine(DoProcess(notReadyProcess));

            var readyElapsed = 0L;
            var notReadyElapsed = 0L;

            const long times = 100L;
            foreach (var _ in Enumerable.Range(0, 10))
            {
                foreach (var __ in Enumerable.Range(0, 10))
                {
                    readyElapsed += DoProcess(readyProcess);
                    notReadyElapsed += DoProcess(notReadyProcess);
                }
                Console.WriteLine(_);
            }

            Console.WriteLine($"   ready:{readyElapsed / times}");
            Console.WriteLine($"notReady:{notReadyElapsed / times}");
        }

        static long DoProcess(Process p)
        {
            var tick = DateTime.Now.Ticks;
            p.Start();
            var output = p.StandardOutput.ReadToEnd();
            var elapsed = long.Parse(output) - tick;
            return elapsed;
        }
    }

実行結果 1 回目

   ready:928692
notReady:945244

実行結果 2 回目

   ready:1124247
notReady:1143800

実行結果 3 回目

   ready:1056758
notReady:1092803

単位(100ナノ秒)

設定を逆にしても期待通りの結果となったので、とりあえず ReadyToRun の効果は期待して良さそうです。
とりあえず設定しておくことにします。

■ 追記

mac で試してみても ReadyToRun した方が早かったので、効果はあるのでしょう。