C#中的[Intrinsic]属性有什么作用? [英] What does the [Intrinsic] attribute in C# do?
问题描述
在Google中快速搜索内在属性c#只会返回有关其他属性的文章,例如 [可序列化]
。显然,这些被称为固有属性。
但是,C#中还有一个本身称为 [Intrinsic]
的属性,我正在尝试弄清楚它到底是什么以及它如何工作。 常见属性上不存在.NET文档的页或据我所知在文档中的其他任何地方。
此属性在.NET Core内部使用例如,放在 System.Numerics.Vectors
文件夹,例如 Vector2_Intrinsics.cs
。代码段:
[固有]
public Vector2(float x,float y)
{
X = x;
Y = y;
}
这就是我所管理的通过github上的 dotnet / corefx 存储库进行非常有限的搜索后即可找到。
[Intrinsic]
标记可以被JIT替换/优化的方法,属性和字段。源代码注释表示类似的内容( IntrinsicAttribute.cs
):
调用方法或对带有此属性标记的字段的引用可能会在某些调用站点被jit固有扩展替换。标记有此属性的类型可以由运行时/编译器特殊处理。
目的
对于核心开发人员来说, [固有的]
至少有两个作用:
- 它通知开发人员可以将标记的字段,方法或属性的代码替换为VM。因此,如果代码更改了,那么更改可能应该在两个地方引入;
- 它用作JIT优化器的标志,以快速识别可以进行优化的方法。 li>
举个简单的例子:JIT优化程序可以将 Enum.HasFlag
替换为在某些情况下进行简单的按位比较,而在其他情况下则不会。为此,它需要将方法标识为 Enum.HasFlag
,检查一些条件并将其替换为更优化的实现。优化器可以按名称标识该方法,但是出于性能原因,最好在执行字符串比较之前通过一个简单的标志筛选出方法。
用法
该属性仅与核心开发人员相关。您仅应在内部类中使用它,并且仅在要为其提出非常特定的JIT级别优化的情况下使用。 [固有]
几乎仅限于一小套广泛使用的.Net类,由于某些原因,它不能通过其他方式进行优化。
:我打算为.NET Core建议一个Color结构,该结构的行为应与其他构建的行为类似-in类型以保持一致。
您可能不应该使用 [Intrinsic]
在您最初的建议中。通过之后,您可以考虑优化,如果您有一个有效的方案,当 Color
将从低级优化中受益时,您可以建议使用 [内在]
的某些方法或属性。
工作原理
当前在核心中使用 [固有]
的方式如下:
-
it被定义为众所周知的属性(
wellknownattributes.h
):case WellKnownAttribute :: Intrinsic :
返回 System.Runtime.CompilerServices.IntrinsicAttribute;
-
VM对其进行解析并设置
IsJitIntrinsic
标记为true的方法(methodtablebuilder.cpp
):if(bmtProp-> fIsHardwareIntrinsic ||(S_OK == GetCustomAttribute(pMethod-> GetMethodSignature()。GetToken(),
WellKnownAttribute :: Intrinsic,
NULL,
NULL)))
{
pNewMD-> SetIsJitIntrinsic();
}
-
此标志用于在方法属性中设置另一个标志(< a href = https://github.com/dotnet/coreclr/blob/master/src/vm/jitinterface.cpp#L6779 rel = nofollow noreferrer>
jitinterface.cpp
):if(pMD-> IsJitIntrinsic())
结果| = 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)
{
//优化成功。真正弹出堆栈。
impPopStack();
impPopStack();
retNode = optTree;
}
else
{
// //在变体中重试优化。
isSpecial = true;
}
休息时间;
}
免责声明:到目前为止如我所知,该属性的行为没有在任何地方正确记录,因此可能会发生变化。上面的描述仅与当前在master中使用的代码相关,核心的这一部分正在积极开发中,将来可以更改整个过程。
历史记录
以下是基于github存储库历史记录的 [Intrinsic]
的简短时间表:
-
在2014年之前的某个时间,
[JitIntrisic]
属性是System的一部分引入的。
,其目标是支持新的处理器指令(请参阅 JitIntrinsicAttribute如何影响代码生成?)。 p> -
2016年6月6日,克里斯·麦肯锡打开了一个问题#5626。 当类型相同时,将enum1.HasFlag(enum2)优化为内联位测试,而无需装箱分配。。当时,
Enum.HasFlag
有一个众所周知的性能问题(请参阅这是什么?使得Enum.HasFlag如此缓慢?)。 -
在处理问题时 Andy Ayers 建议引入一种通用机制来引入JIT内在函数()
-
这导致了两个请求:新的jit内在支持介绍了
[Intrinsic] $ c的通用机制$ c>和 JIT:优化Enum.HasFlag 为
Enum.HasFlag
。我建议仔细阅读这两个方法,因为它们对于[固有的]
所带来的变化具有很好的说明性。 -
稍后,在讨论移动> $$ c $ Vector CoreLib的类,建议在任何地方都不要使用
[JitIntrinsic]
,应该将其替换/删除:
@jkotas :我们不需要JitIntrinsicAttribute。据我所知,该属性是面向未来的证明,从未用于任何实际用途。我们应该删除它,而改用CoreLib的IntrinsicAttribute。
- 立即,
[JitIntrinsic]
被删除,并替换为[Intrinsic]
(用IntrinsicAttribute替换JitIntrinsicAttribute )。这就是该属性出现在Vector2
中的方式。
A quick Google search for "instrinsic attribute c#" only returns articles about other attributes, such as [Serializable]
. Apparently these are called "intrinsic attributes".
However, there is also an attribute in C# that is itself called [Intrinsic]
and I'm trying to figure out what exactly it is and how it works. It doesn't exist on the common attributes page of the .NET Documentation, or anywhere else in the documentation as far as I can see.
This attribute is used inside of .NET Core in several places, for example, in the System.Numerics.Vectors
folder, such as Vector2_Intrinsics.cs
. Code snippet:
[Intrinsic]
public Vector2(float x, float y)
{
X = x;
Y = y;
}
Here's what I've managed to find after a very limited search through dotnet/corefx repository on github.
[Intrinsic]
marks methods, properties and fields that can be potentially replaced/optimized by JIT. Source code comments say something similar (IntrinsicAttribute.cs
):
Calls to methods or references to fields marked with this attribute may be replaced at some call sites with jit intrinsic expansions. Types marked with this attribute may be specially treated by the runtime/compiler.
Purpose
For core developers, [Intrinsic]
serves at least two purposes:
- it notifies the developer that the code of the marked field, method or property can be replaced by VM. So, if the code changes, the change should probably be introduced in both places;
- it is used as a flag for JIT-optimizer to quickly identify methods that can potentially be optimized.
To give a rough example: JIT-optimizer can replace Enum.HasFlag
with a simple bitwise comparison in some cases and not in the others. To do this it needs to identify the method as Enum.HasFlag
, check some conditions and replace it with a more optimal implementation. The optimizer can identify the method by name, but, for performance reasons, it's better to filter out methods by a simple flag before performing string comparisons.
Usage
The attribute is only relevant to core developers. You should only use it in an internal class and only in the case when you want to propose very specific JIT-level optimizations for it. [Intrinsic]
is pretty much restricted to a small set of widely used .Net classes, that, for some reason, can't be optimized by other means.
from the comments: I'm planning to propose a Color struct for .NET Core which needs to behave similarly to other built-in types for consistency.
You should probably not use [Intrinsic]
in your initial proposal. After it passes, you can think about optimization, and if you have a valid scenario when Color
will benefit from low level optimizations, you can suggest using [Intrinsic]
on some of its methods or properties.
How It Works
Here's how [Intrinsic]
is currently used in core:
it is defined as a well-known attribute (
wellknownattributes.h
):case WellKnownAttribute::Intrinsic: return "System.Runtime.CompilerServices.IntrinsicAttribute";
VM parses it and sets the
IsJitIntrinsic
flag to true for a method (methodtablebuilder.cpp
):if (bmtProp->fIsHardwareIntrinsic || (S_OK == GetCustomAttribute(pMethod->GetMethodSignature().GetToken(), WellKnownAttribute::Intrinsic, NULL, NULL))) { pNewMD->SetIsJitIntrinsic(); }
this flag is used to set another flag in method attributes (
jitinterface.cpp
):if (pMD->IsJitIntrinsic()) result |= CORINFO_FLG_JIT_INTRINSIC;
this flag is later used to filter out methods which are obviously not 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
then callslookupNamedIntrinsic
to identify (mostly by name) methods that really (not just potentially) should be optimized;after all of that
importer
can perform optimizations based on method. For example, optimization forEnum.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; }
DISCLAIMER: as far as I can tell, the attribute's behaviour is not properly documented anywhere and, thus, is subject for change. The description above is only relevant to code currently in master, this part of core is actively developed and the whole process can be changed in the future.
History
Here's a short timeline of [Intrinsic]
based on github repository history:
At some time before 2014
[JitIntrisic]
attribute was introduced as a part ofSystem.Numerics
with a goal to support new processor instructions (see How does JitIntrinsicAttribute affect code generation?).On June 6, 2016, Chris McKinsey opened an issue #5626. "Optimize enum1.HasFlag(enum2) into inline bittest without boxing allocations when types are the same". At the time,
Enum.HasFlag
had a well-known performance issues (see What is it that makes Enum.HasFlag so slow?).While working on the issue Andy Ayers suggested to introduce a universal mechanism to introduce JIT intrinsics (Issue #13813: Add more flexible method for specifying jit instrinsics)
This led to two pull requests: New jit intrinsic support introduced the general mechanics for
[Intrinsic]
and JIT: optimize Enum.HasFlag implemented it forEnum.HasFlag
. I suggest going through both of them as they are extremely illustrative on the changes that come with[Intrinsic]
.Later, during the discussion about moving
Vector
classes to the CoreLib it was suggested that[JitIntrinsic]
isn't used anywhere and should be replaced/removed:
@jkotas: We should not need the JitIntrinsicAttribute. As far as I know, this attribute was future proofing, never used for anything real. We should delete it, and use the IntrinsicAttribute from CoreLib instead.
- Promptly,
[JitIntrinsic]
was removed and replace by[Intrinsic]
(Replace JitIntrinsicAttribute with IntrinsicAttribute). That's how this attribute came to be inVector2
.
这篇关于C#中的[Intrinsic]属性有什么作用?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!