Что делает атрибут [Intrinsic] в C #? - PullRequest
8 голосов
/ 31 мая 2019

Быстрый поиск в Google по запросу "instrinsic attribute c #" возвращает только статьи о других атрибутах, таких как [Serializable].По-видимому, они называются «внутренними атрибутами».

Однако в C # есть также атрибут, который сам по себе называется [Intrinsic], и я пытаюсь выяснить, что это такое и как оно работает.Насколько я вижу, его нет на странице общие атрибуты документации .NET или где-либо еще в документации.

Этот атрибут используется внутри .NET Coreв нескольких местах, например, в папке System.Numerics.Vectors, например Vector2_Intrinsics.cs.Фрагмент кода:

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

Ответы [ 2 ]

4 голосов
/ 31 мая 2019

Вот что мне удалось найти после очень ограниченного поиска в репозитории dotnet / corefx на github.

[Intrinsic] отмечает методы, свойства и поля, которые могут быть потенциально заменены / оптимизированы JIT. Комментарии исходного кода говорят что-то похожее (IntrinsicAttribute.cs):

Вызовы методов или ссылки на поля, отмеченные этим атрибутом, могут быть заменены на некоторых сайтах вызовов внутренними расширениями jit. Типы, отмеченные этим атрибутом, могут специально обрабатываться средой выполнения / компилятором.

Цель

Для разработчиков ядра [Intrinsic] служит как минимум двум целям:

  • уведомляет разработчика о том, что код помеченного поля, метода или свойства может быть заменен виртуальной машиной. Таким образом, если код изменяется, изменение, вероятно, должно быть введено в обоих местах;
  • он используется в качестве флага для JIT-оптимизатора для быстрой идентификации методов, которые потенциально могут быть оптимизированы.

В качестве грубого примера: JIT-оптимизатор может заменить Enum.HasFlag простым побитовым сравнением в некоторых случаях, а не в других. Для этого необходимо определить метод как Enum.HasFlag, проверить некоторые условия и заменить его более оптимальной реализацией. Оптимизатор может идентифицировать метод по имени, но по соображениям производительности лучше отфильтровать методы по простому флагу перед выполнением сравнения строк.

Использование

Атрибут актуален только для разработчиков ядра. Вы должны использовать его только во внутреннем классе и только в том случае, если вы хотите предложить для него очень специфические оптимизации уровня JIT. [Intrinsic] в значительной степени ограничен небольшим набором широко используемых классов .Net, которые по некоторым причинам не могут быть оптимизированы другими средствами.

из комментариев : Я планирую предложить структуру Color для .NET Core, которая должна вести себя аналогично другим встроенным типам для согласованности.

Возможно, вам не следует использовать [Intrinsic] в вашем первоначальном предложении. После того, как он пройдет, вы можете подумать об оптимизации, и если у вас есть действительный сценарий, когда Color выиграет от низкоуровневых оптимизаций, вы можете предложить использовать [Intrinsic] в некоторых из его методов или свойств.

Как это работает

Вот как [Intrinsic] в настоящее время используется в ядре:

  • определяется как общеизвестный атрибут (wellknownattributes.h):

    case WellKnownAttribute::Intrinsic:
        return "System.Runtime.CompilerServices.IntrinsicAttribute";  
    
  • ВМ анализирует ее и устанавливает для флага 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);
    
  • impIntrinsic затем вызывает lookupNamedIntrinsic для определения (в основном по имени) методов, которые действительно (а не только потенциально) следует оптимизировать;

  • после всего этого 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;
        }
    

ОТКАЗ ОТ ОТВЕТСТВЕННОСТИ: насколько я могу судить, поведение атрибута нигде не документировано должным образом и, следовательно, может быть изменено. Вышеприведенное описание относится только к коду, находящемуся в настоящее время в master, эта часть ядра активно разрабатывается, и весь процесс может быть изменен в будущем.

История

Вот краткий график времени [Intrinsic], основанный на истории хранилища github:

@ jkotas : нам не нужен атрибут JitIntrinsic. Насколько я знаю, этот атрибут был проверкой на будущее, никогда не использовался для чего-то реального. Мы должны удалить его и использовать вместо него IntrinsicAttribute из CoreLib.

0 голосов
/ 31 мая 2019

Пояснение:

Специальные типы указываются компилятору с помощью Настраиваемый атрибут IntrinsicAttribute. Если тип помечен с Атрибут IntrinsicAttribute, компилятор не знает, что реализация для данного типа будет присутствовать во время выполнения. Методы для типов, помеченных как Intrinsic, могут объявлять методы как extern, и в этом случае реализация предполагается доступной в во время выполнения.

Источник: компилятор MSIL to JavaScript, раздел 4.4.1.1

Ссылка: http://tenpow.com/Academics/MSIL2JS/MSIL2JS.pdf

В общем, я бы посоветовал не заботиться об этом и не использовать его для собственных занятий.

...