zoukankan      html  css  js  c++  java
  • CLR泛型和代码共享

    First up – 泛型的高级位和代码共享

    我们基于变量的类型,做了代码共享和匹配的混合。

    对于引用类型的变量,泛型方法实例化了代码共享。

    对于内置类型和值类型,包括枚举,泛型方法是专业化的。

    什么是代码共享?

    就泛型而言,代码共享是指有两个或多个“兼容”方法的实例指向了同一段x86代码。例如Foo.M<MyClass1>和Foo.M(MyClass2)共享同样的x86代码,MyClass1和MyClass2是引用类型。

    简单的历史 – 我们在v1.0和v1.1里,同样对数组类型的引用类型做了代码共享。

    快速回顾EE(执行引擎)数据结构

    关于CLR的执行引擎数据结构,在SSCLI Essentials书里有很好的解释。概述如下:

    在堆上所有的对象,都有一个固定大小的指针指向方法表,方法表描述了对象类型标识符(你实际上通过RuntimeTypeHandle得到了托管代码的表现形式)。方法表不仅包含了指向EE结构的指针,更重要的是,类型方法的列表和他们 表现得代码指针。这些指针可以指向x86代码,也可以指向JIT stub(可以调用JIT如果该方法还没有被JIT)。方法表广泛的用于类型定义。

    MethodDesc是一个用于描述方法的小结构体。每个方法都有一个代表性的MehodDesc 结构体,尽管没有被运行时广泛的使用,除非你尝试使用迟绑定(反射)。在运行时里MehodDesc有不同的类型,但是对于发送(post)而言,我们可以假设他们都一样。

    调用一个实例的方法在概念上就像这样:从自身的指针开始,指向方法表(方法指针代码的索引),然后调用代码指针,把自己的地址作为变量传递,这是每个x86调用的惯例。调用静态方法实际上是做同样的事情,只是没有“this”指针作为变量传递。当然了,JIT可以生成代码直接调用各种指针- 在JIT时,他做了一个很大的方法的表用于查找代码指针。

    中间语言的泛型方法

    当我们在描述泛型的时候,我们有一个未知的问题-当我们的JIT编译时,我们对局部变量 T 做了什么处理? 怎样生成x86的代码?

    让我们先考虑下如下的代码段:

    class Foo

    {

          [MethodImpl(MethodImplOptions.NoInlining)]

          public void M1<T>()

          {

                Console.WriteLine(typeof(T));

          }

    }

    Foo f1 = new Foo().M1<string>();

    Foo f2 = new Foo().M1<object>();

    当我们不知道T是什么类型的时候,M1<T>在代码中的表现形式是什么?我们实际上指定了类型参数“!!arity”,arity是泛型类型参数的索引(0,1…)

    Foo.M1<T>的中间语言如下所示:

    // IL Code for Foo.M1<T>

    .method public hidebysig instance void  M1<([mscorlib]System.Object) T>() cil managed noinlining

    {

      // Code size       17 (0x11)

      .maxstack  8

      IL_0000:  ldtoken    !!0

      IL_0005:  call       class [mscorlib]System.Type [mscorlib]System.Type::GetTypeFromHandle(valuetype [mscorlib]System.RuntimeTypeHandle)

      IL_000a:  call       void [mscorlib]System.Console::WriteLine(object)

      IL_000f:  nop

      IL_0010:  ret

    } // end of method Foo::M1

    我们不用对类型做任何假设,该方法将会被传递。你可以想象,在抽象的机器世界里,调用这个方法,我们将基于传递的泛型变量,用字符串或者对象将!!0替换掉。这个场景与JIT编译时是很相近的。

    进入泛型方法与代码共享:

    查看代码,我有Foo类型的两个实例,调用本质上不同的方法(例如:M1<object> 与M1<string>是不一样的,同样也是通过反射来表现)。通过代码共享,这两个方法的实例是指向同一段x86的代码。

    正如这样:当我们遇到这段代码: Foo f1 = new Foo().M1<string>(), JIT第一次对这个方法进行编译,我们得到如上的中间编译语言,使用一个指针替代!!0去调用运行时,以获得该类型的类型信息,将其传递到泛型变量slot()。这个指针我们是从什么地方获得的了?好吧,这来自于一个奇怪的调用惯例:我们为这个调用提供了一个隐藏的变量,是一个指向了运行数据结构体的指针为我们提供了信息。

    带来的隐藏变量的数据结构

    一个指向什么地方的指针?好吧,在一个指定的实例里,我们只要知道提供给泛型方法的类型参数, 我们创造并且传递给MehodDesc指针用于方法的特殊声明。对于Foo.M1<string> 我们传递到MethodDesc的事M1<string>,对于Foo().M1<object>,我们传递到MethodDesc的是M1<object>。MethodDesc实际上包含了用于确定!!0的所有信息。JIT编译的x86代码将会从MethodDesc指针里得到类型信息。

    所以,对于上面的例子而言,JIT 遇到“Idtoken !!0”,编译的x86代码从隐藏变量里获得传进去的变量指针。对于string和object都是一样工作的,因为我们是间接的获得指针,而不是指定的string和object。

    我们在什么时候需要“隐藏”变量

    我们需要生成隐藏变量用于一些例子,这些例子不能从可用的x86执行时获得类型信息。如下是一些例子,伴随着隐藏的数据结构体:

    1. Foo<T> static M()                   == TypeHandle

    2. Foo<T> static M<T>              == MethodDesc

    3. Foo M<T>                               == MethodDesc

    4. Foo static M<T>                     == MethodDesc

    第四种类型是最有趣的,我们实际上需要TypeHandle和MethodDesc, 但是JIT知道我们可以从MehodDesc获得TypeHandle生成代码(这是一种间接的情况,正如我在SSCLI描述的一样)。

    没有指针的一种情况是Foo<T> M(),因为M是一个实例化方法,并且我们已经按照惯例传递了“this”指针,我们可以从”this”获得变量T里获得变量类型。

    对于这些问题,为什么传递MethodDesc/TypeHandle,为什么我们不仅仅将类型作为隐藏变量传递。很好的问题,对于有不知一个泛型参数的情况,这有效的减少了参数传递的调用时间。例如M<U,W,Z>,MethodDesc已经很好的描述了他,所以这样更容易,更有效的将他传递到MD指针,并且合适的生成index代码的索引。

    我们为什么对内置类型和值类型缺少代码共享?

    从技术层面上讲,我们可以对内置类型和值类型进行代码共享,但是很明显这样效率很低。为了让他变得更容易理解:

    考虑:

    Foo{M<T>() {}}

    new Foo().M<int>();

    new Foo().M<double>();

    JIT实际上会生成两个分离的代码段用于实例化,为什么?好吧,内置类型和值类型是在栈上的,不需要方法表的引用(除非它们被封装了)。通常,不同的值类型/内置类型有不同的数据大小,整形和双精度型就是个很好的例子。JIT对于未知的数据尺寸,不能生成x86的代码,所以将他们特殊化。

    运行时可以通过封装值类型/内置类型来实现共享,但这是否阻止了对泛型不必要的封装?

    部分特定化

    我不是很确定如果我们的CLR团队是否调用了部分特定化,但是我将调用它用于博客的发送。对于具有值类型和引用类型的例子,我们对值类型特殊化,并且链接引用类型共享的代码。

    Foo<int>.M<string> 和Foo<int>.M<object> 是相同的代码. 我们共享了Foo<int> 里的M<T>。

    引用:http://blogs.msdn.com/b/joelpob/archive/2004/11/17/259224.aspx

  • 相关阅读:
    北斗对时设备(GPS校时产品)在数字城市系统中的应用
    京准电子,北斗授时产品取代GPS对时设备服务各行业
    北斗校时系统(GPS授时设备)几种时间同步方法
    网络对时服务器(NTP校时服务器)应用港口信息化系统
    关于医院NTP授时服务器(NTP校时服务器)部署工作
    变电站里NTP校时器(gps网络时间服务器)产品的应用
    C++学习(二十九)(C语言部分)之 顺序表
    C++学习(二十八)(C语言部分)之 文件操作
    C++学习(二十七)(C语言部分)之 预处理命令
    C++学习(二十六)(C语言部分)之 结构体3(联合,枚举)
  • 原文地址:https://www.cnblogs.com/longcloud/p/3134439.html
Copyright © 2011-2022 走看看