web-dev-qa-db-ja.com

C#の[組み込み]属性は何をしますか?

Googleで「組み込み属性c#」をすばやく検索すると、[Serializable]などの他の属性に関する記事のみが返されます。どうやらこれらは「固有属性」と呼ばれています。

ただし、それ自体が[Intrinsic]と呼ばれるC#の属性もあり、それが正確に何であり、どのように機能するかを理解しようとしています。 .NETドキュメントの common attributes ページ、またはドキュメント内の他のどこにも、私が見る限り存在しません。

この属性は、.NET Core内のいくつかの場所で使用されます。たとえば、 System.Numerics.Vectors などの Vector2_Intrinsics.cs フォルダ内です。コードスニペット:

[Intrinsic]
public Vector2(float x, float y)
{
    X = x;
    Y = y;
}
8
Aaron Franke

これが、githubの dotnet/corefx リポジトリを非常に限定的に検索した結果、私が見つけたものです。

[Intrinsic]は、JITによって置換または最適化される可能性のあるメソッド、プロパティ、およびフィールドを示します。ソースコードのコメントは、似たようなことを言っています( IntrinsicAttribute.cs ):

メソッドへの呼び出しまたはこの属性でマークされたフィールドへの参照は、いくつかの呼び出しサイトでjit組み込み展開に置き換えられる場合があります。この属性でマークされた型は、ランタイム/コンパイラによって特別に処理される場合があります。

目的

コア開発者にとって、[Intrinsic]は少なくとも2つの目的を果たします。

  • マークされたフィールド、メソッド、またはプロパティのコードをVMで置き換えることができることを開発者に通知します。したがって、コードが変更された場合は、おそらく両方の場所で変更を導入する必要があります。
  • jITオプティマイザーのフラグとして使用され、最適化できる可能性のあるメソッドをすばやく識別します。

大まかな例を示すと、JITオプティマイザーはEnum.HasFlagを単純なビットごとの比較に置き換えることができる場合とそうでない場合があります。これを行うには、メソッドをEnum.HasFlagとして識別し、いくつかの条件を確認して、より最適な実装に置き換える必要があります。オプティマイザはメソッドを名前で識別できますが、パフォーマンス上の理由から、文字列比較を実行する前に、単純なフラグでメソッドを除外することをお勧めします。

使用法

この属性は、コア開発者にのみ関連しています。これは、内部クラスでのみ使用し、非常に具体的なJITレベルの最適化を提案する場合にのみ使用してください。 [Intrinsic]は、広く使用されている.Netクラスの小さなセットにかなり制限されており、何らかの理由で他の方法では最適化できません。

コメントから:一貫性のために他の組み込み型と同様に動作する必要がある.NET CoreのColor構造体を提案する予定です。

おそらく最初の提案では[Intrinsic]を使用しないでください。合格したら、最適化について考えることができます。Colorが低レベルの最適化から利益を得る有効なシナリオがある場合は、その一部のメソッドまたはプロパティで[Intrinsic]の使用を提案できます。

使い方

コアでの[Intrinsic]の現在の使用方法は次のとおりです。

  • 既知の属性として定義されています( wellknownattributes.h ):

    case WellKnownAttribute::Intrinsic:
        return "System.Runtime.CompilerServices.IntrinsicAttribute";  
    
  • VMはそれを解析し、メソッドのIsJitIntrinsicフラグをtrueに設定します( methodtablebuilder.cpp ):

    if (bmtProp->fIsHardwareIntrinsic || (S_OK == GetCustomAttribute(pMethod->GetMethodSignature().GetToken(),
                                                WellKnownAttribute::Intrinsic,
                                                NULL,
                                                NULL)))
    {
        pNewMD->SetIsJitIntrinsic();
    }          
    
  • このフラグは、メソッド属性に別のフラグを設定するために使用されます( jitinterface.cpp ):

    if (pMD->IsJitIntrinsic())
        result |= CORINFO_FLG_JIT_INTRINSIC;
    
  • このフラグは後で明らかに組み込まれていないメソッドを除外するために使用されます( importer.cpp ):

    if ((mflags & (CORINFO_FLG_INTRINSIC | CORINFO_FLG_JIT_INTRINSIC)) != 0)
    {
        const bool isTail = canTailCall && (tailCall != 0);
    
        call = impIntrinsic(newobjThis, clsHnd, methHnd, sig, mflags, pResolvedToken->token, readonlyCall, isTail,
                            pConstrainedResolvedToken, callInfo->thisTransform, &intrinsicID, &isSpecialIntrinsic);
    
  • 次に、impIntrinsiclookupNamedIntrinsic を呼び出して、(ほとんどの場合、潜在的にではなく)最適化する必要があるメソッドを(ほとんどは名前で)識別します。

  • 結局、importerはメソッドに基づいて最適化を実行できます。たとえば、Enum.HasFlagの最適化( importer.cpp ):

     case NI_System_Enum_HasFlag:
        {
            GenTree* thisOp  = impStackTop(1).val;
            GenTree* flagOp  = impStackTop(0).val;
            GenTree* optTree = gtOptimizeEnumHasFlag(thisOp, flagOp);
    
            if (optTree != nullptr)
            {
                // Optimization successful. Pop the stack for real.
                impPopStack();
                impPopStack();
                retNode = optTree;
            }
            else
            {
                // Retry optimizing this during morph.
                isSpecial = true;
            }
    
            break;
        }
    

免責事項:私の知る限り、属性の動作はどこにも適切に文書化されていないため、変更される可能性があります。上記の説明は、現在マスターにあるコードにのみ関連しています。コアのこの部分は積極的に開発されており、プロセス全体は将来変更される可能性があります。

歴史

Githubリポジトリの履歴に基づく[Intrinsic]の短いタイムラインは次のとおりです。

@ jkotas :JitIntrinsicAttributeは必要ありません。私の知る限り、この属性は将来を見据えたものであり、実際に使用されることはありません。それを削除し、代わりにCoreLibのIntrinsicAttributeを使用する必要があります。

  • すぐに、[JitIntrinsic]が削除され、[Intrinsic]に置き換えられました( JitIntrinsicAttributeをIntrinsicAttribute で置き換えます)。これが、この属性がVector2に含まれるようになった方法です。
12
default locale

説明:

コンパイラーには、IntrinsicAttributeカスタム属性を使用して特別なタイプが示されます。型にIntrinsicAttribute属性で注釈が付けられている場合、コンパイラーは、指定された型の実装が実行時に存在することを認識していません。組み込みとしてマークされた型のメソッドは、メソッドがexternであることを宣言できます。その場合、実装は実行時に利用可能であると見なされます。

ソース:MSILからJavaScriptコンパイラ、セクション4.4.1.1

リンク: http://tenpow.com/Academics/MSIL2JS/MSIL2JS.pdf

一般に、私はそれを気にしないで、自分のクラスで使用しないことをお勧めします。

0
Jaime