Open1

💭 C# の out / ref / in 等の修飾子はインライン化に影響を与えますか?

サトー™ @sator_imagingサトー™ @sator_imaging

分析の文責 @ GPT-4o

.NET ランタイムでは outrefin などのパラメーター修飾子はインライン化(inlining)に影響を与えることがあります。以下に簡潔な説明とサンプルコードを示します。

.NET ランタイムにおけるパラメーター修飾子とインライン化の関係

概要

  • .NET JIT(Just-In-Time コンパイラ)はメソッドのインライン化を最適化の一環として行います。
  • refoutin 修飾子が付いたパラメーターは、通常の値渡しよりも複雑な動作になるため、インライン化されにくくなる傾向があります。

各修飾子の特徴とインライン化への影響

修飾子 意味 インライン化への影響
ref 参照渡し(読み書き可) 高い影響:インライン化を抑制する可能性あり
out 出力専用の参照渡し 高い影響:インライン化を抑制する可能性あり
in 読み取り専用の参照渡し(C# 7.2〜) 中程度の影響:読み取りのみでも参照なので慎重に扱われる

JIT コンパイラは安全性や最適化とのトレードオフの観点から、これらの修飾子が付いているとメソッドのインライン化を避ける場合があります。

サンプルコード

以下の C# コードで、各修飾子付きメソッドのインライン化の影響を JIT のログで確認できます(RyuJIT を使用):

using System;
using System.Runtime.CompilerServices;

public class InlineTest
{
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static void RefMethod(ref int x)
    {
        x *= 2;
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static void OutMethod(out int x)
    {
        x = 42;
    }

    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public static void InMethod(in int x)
    {
        Console.WriteLine(x);
    }

    public static void Main()
    {
        int a = 5;
        RefMethod(ref a);

        int b;
        OutMethod(out b);

        int c = 10;
        InMethod(in c);
    }
}

JIT による最終的な判断は、対象のプラットフォーム(x64, ARM 等)や .NET のバージョン、さらにはメソッドのサイズ・複雑性などによっても変わります。

インライン化の確認方法

インライン化の可否は Tiered Compilation にも影響を受けるため、検証には以下のオプションをつけて起動するのが有効です:

DOTNET_TieredPGO=1
DOTNET_JitDump=*

https://learn.microsoft.com/en-us/dotnet/core/run-time-config/jit-compilation

JIT によるインライン化の可否は、実行時にログを出力することで確認できます。以下の環境変数を指定して実行すると、JIT の出力が得られます。

DOTNET_JitDump=InlineTest
DOTNET_TieredPGO=1
dotnet run

JIT の出力には、各メソッドに対して Inline CandidateWill not inline といった情報が記載されます。

より詳細な分析を行うには、以下のツールが役立ちます:

まとめ

  • refoutin は JIT にとって「副作用」や「最適化の難しさ」を意味するため、インライン化が拒否される原因になります。
  • ただし、JIT の進化(特に .NET 7 以降の Tiered PGO)により、条件によってはインライン化されることもあります。
  • 実際のインライン化の可否は、JIT の判断に委ねられており、コードの規模や使用方法、ランタイムの最適化機能に強く依存します。