为什么在幕后被TypedReference?它是如此之快,安全......几乎是不可思议的! [英] Why is TypedReference behind the scenes? It's so fast and safe... almost magical!

查看:500
本文介绍了为什么在幕后被TypedReference?它是如此之快,安全......几乎是不可思议的!的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

警告:这个问题有点异端宗教...程序员总是好的做法守法,请不要阅读。 :)

Warning: This question is a bit heretical... religious programmers always abiding by good practices, please don't read it. :)

有谁知道为什么使用<一个href=\"http://msdn.microsoft.com/en-us/library/system.typedreference%28v=VS.80%29.aspx\">TypedReference如此灰心(隐式地缺乏文档)?

Does anyone know why the use of TypedReference is so discouraged (implicitly, by lack of documentation)?

我找到了很大的用途,例如通过函数不应该是通用的传递泛型参数时(使用对象时,如可能是矫枉过正或慢,如果你需要一个数值型),因为当你需要一个不透明的指针,或当你需要快速访问数组的元素,其规格在运行时发现(使用 Array.InternalGetReference )。由于CLR甚至不允许这种类型的不正确使用,为什么它不鼓励?它似乎没有不安全或任何...

I've found great uses for it, such as when passing generic parameters through functions that shouldn't be generic (when using an object might be overkill or slow, if you need a value type), for when you need an opaque pointer, or for when you need to access an element of an array quickly, whose specs you find at runtime (using Array.InternalGetReference). Since the CLR doesn't even allow incorrect usage of this type, why is it discouraged? It doesn't seem to be unsafe or anything...

其他用途,我发现了 TypedReference

专业,在C#泛型(这是类型安全):

"Specializing" generics in C# (this is type-safe):

static void foo<T>(ref T value)
{
    //This is the ONLY way to treat value as int, without boxing/unboxing objects
    if (value is int)
    { __refvalue(__makeref(value), int) = 1; }
    else { value = default(T); }
}

写作code,与通用指针工作(这是的非常的不安全如果误,但快速和安全的,如果正确使用):

Writing code that works with generic pointers (this is very unsafe if misused, but fast and safe if used correctly):

//This bypasses the restriction that you can't have a pointer to T,
//letting you write very high-performance generic code.
//It's dangerous if you don't know what you're doing, but very worth if you do.
static T Read<T>(IntPtr address)
{
    var obj = default(T);
    var tr = __makeref(obj);

    //This is equivalent to shooting yourself in the foot
    //but it's the only high-perf solution in some cases
    //it sets the first field of the TypedReference (which is a pointer)
    //to the address you give it, then it dereferences the value.
    //Better be 10000% sure that your type T is unmanaged/blittable...
    unsafe { *(IntPtr*)(&tr) = address; }

    return __refvalue(tr, T);
}

写的方法的的的sizeof 指令,它可以是偶尔有用的版本:

Writing a method version of the sizeof instruction, which can be occasionally useful:

static class ArrayOfTwoElements<T> { static readonly Value = new T[2]; }

static uint SizeOf<T>()
{
    unsafe 
    {
        TypedReference
            elem1 = __makeref(ArrayOfTwoElements<T>.Value[0] ),
            elem2 = __makeref(ArrayOfTwoElements<T>.Value[1] );
        unsafe
        { return (uint)((byte*)*(IntPtr*)(&elem2) - (byte*)*(IntPtr*)(&elem1)); }
    }
}

写作传递想要避免拳状态参数的方法:

Writing a method that passes a "state" parameter that wants to avoid boxing:

static void call(Action<int, TypedReference> action, TypedReference state)
{
    //Note: I could've said "object" instead of "TypedReference",
    //but if I had, then the user would've had to box any value types
    try
    {
        action(0, state);
    }
    finally { /*Do any cleanup needed*/ }
}

那么,为什么这样的劝阻的用途(由缺乏文件)?有什么特别的安全原因?看来绝对安全和可核查的,如果它不是用指针(这是不是安全或可验证反正)混合...

So why are uses like this "discouraged" (by lack of documentation)? Any particular safety reasons? It seems perfectly safe and verifiable if it's not mixed with pointers (which aren't safe or verifiable anyway)...

更新:

样code来表示,事实上, TypedReference 可以是快两倍(或更多):

Sample code to show that, indeed, TypedReference can be twice as fast (or more):

using System;
using System.Collections.Generic;
static class Program
{
    static void Set1<T>(T[] a, int i, int v)
    { __refvalue(__makeref(a[i]), int) = v; }

    static void Set2<T>(T[] a, int i, int v)
    { a[i] = (T)(object)v; }

    static void Main(string[] args)
    {
        var root = new List<object>();
        var rand = new Random();
        for (int i = 0; i < 1024; i++)
        { root.Add(new byte[rand.Next(1024 * 64)]); }
        //The above code is to put just a bit of pressure on the GC

        var arr = new int[5];
        int start;
        const int COUNT = 40000000;

        start = Environment.TickCount;
        for (int i = 0; i < COUNT; i++)
        { Set1(arr, 0, i); }
        Console.WriteLine("Using TypedReference:  {0} ticks",
                          Environment.TickCount - start);
        start = Environment.TickCount;
        for (int i = 0; i < COUNT; i++)
        { Set2(arr, 0, i); }
        Console.WriteLine("Using boxing/unboxing: {0} ticks",
                          Environment.TickCount - start);

        //Output Using TypedReference:  156 ticks
        //Output Using boxing/unboxing: 484 ticks
    }
}

(编辑:我编辑上面的基准,因为该职位的最后一个版本中使用的code [我忘了去改变它,释放],并把没有pressure对GC的调试版本。此版本是更现实一点,我的系统上,它与 TypedReference 更快平均的三倍以上。)

( I edited the benchmark above, since the last version of the post used a debug version of the code [I forgot to change it to release], and put no pressure on the GC. This version is a bit more realistic, and on my system, it's more than three times faster with TypedReference on average.)

推荐答案

简短的回答:可移植性

__ arglist中 __ makeref __ REFVALUE 是的语言扩展的,并在C#语言规范无证,使用的结构来实现它们的引擎盖下(可变参数调用约定, TypedReference 键入 arglist中 refanytype mkanyref refanyval 指令)中的 CLI规范(ECMA-335)在 VARARG库

While __arglist, __makeref, and __refvalue are language extensions and are undocumented in the C# Language Specification, the constructs used to implement them under the hood (vararg calling convention, TypedReference type, arglist, refanytype, mkanyref, and refanyval instructions) are perfectly documented in the CLI Specification (ECMA-335) in the Vararg library.

可变参数库中被定义使得它很清楚,它们主要是为了支持可变长度参数列表和其他人并不多。可变参数列表在平台上很少使用不需要使用可变参数外部C code至接口。出于这个原因,该可变参数库不是任何的CLI配置文件的一部分。合法的CLI实现可能选择不支持可变参数库,因为它不是在CLI内核配置文件包括:

Being defined in the Vararg Library makes it quite clear that they are primarily meant to support variable-length argument lists and not much else. Variable-argument lists have little use in platforms that don't need to interface with external C code that uses varargs. For this reason, the Varargs library is not part of any CLI profile. Legitimate CLI implementations may choose not to support Varargs library as it's not included in the CLI Kernel profile:

可变参数的功能集支持可变长度参数列表和运行时类型的指针。

4.1.6 Vararg

The vararg feature set supports variable-length argument lists and runtime-typed pointers.

如果省略了:任何试图引用与可变参数调用约定或可变参数方法相关的签名的编码方法(请参阅分区II)应抛出 System.NotImplementedException 例外。使用CIL说明方法 arglist中 refanytype mkrefany ,和 refanyval 应抛出 System.NotImplementedException 例外。异常的precise定时没有规定。类型 System.TypedReference 不需要定义。

If omitted: Any attempt to reference a method with the vararg calling convention or the signature encodings associated with vararg methods (see Partition II) shall throw the System.NotImplementedException exception. Methods using the CIL instructions arglist, refanytype, mkrefany, and refanyval shall throw the System.NotImplementedException exception. The precise timing of the exception is not specified. The type System.TypedReference need not be defined.

FieldInfo.GetValueDirect FieldInfo.SetValueDirect 不会基类库的一部分。需要注意的是有.NET Framework类库和基础类库之间的差异。 BCL是为CLI / C#的一致性实现所需要的唯一的事情,并在 ECMA被记录TR / 84 。 (事实上​​,字段信息本身是反射库的一部分,这是不包括在CLI内核配置文件其一)。

Update (reply to GetValueDirect comment):

FieldInfo.GetValueDirect are FieldInfo.SetValueDirect are not part of Base Class Library. Note that there's a difference between .NET Framework Class Library and Base Class Library. BCL is the only thing required for a conforming implementation of the CLI/C# and is documented in ECMA TR/84. (In fact, FieldInfo itself is part of the Reflection library and that's not included in CLI Kernel profile either).

只要你使用的方法外BCL,你给了一点便携性(这正在成为non-.NET CLI实施像Silverlight和MonoTouch的来临越来越重要)。即使实现想要增加compatiblility与Microsoft .NET框架类库,它可以简单地提供 GetValueDirect SetValueDirect 服用 TypedReference 未做 TypedReference 运行时(基本上特殊处理,使它们相当于其对象同行没有的性能优势)。

As soon as you use a method outside BCL, you are giving up a bit of portability (and this is becoming increasingly important with the advent of non-.NET CLI implementations like Silverlight and MonoTouch). Even if an implementation wanted to increase compatiblility with the Microsoft .NET Framework Class Library, it could simply provide GetValueDirect and SetValueDirect taking a TypedReference without making the TypedReference specially handled by the runtime (basically, making them equivalent to their object counterparts without the performance benefit).

如果他们记录在C#中,将有至少一对夫妇的影响:

Had they documented it in C#, it would have had at least a couple implications:


  1. 就像任何功能,它的可能的成为路障新的功能,特别是因为这一个并不真正适合于C#的设计和要求怪异的语法扩展,并通过一类特殊处理运行。

  2. C#的所有实现必须以某种方式实现这个功能,它不一定琐碎/可能不上CLI之上运行在所有或者在没有可变参数一个CLI上运行的C#实现。

  1. Like any feature, it may become a roadblock to new features, especially since this one doesn't really fit in the design of C# and requires weird syntax extensions and special handing of a type by the runtime.
  2. All implementations of C# have to somehow implement this feature and it's not necessarily trivial/possible for C# implementations that don't run on top of a CLI at all or run on top of a CLI without Varargs.

这篇关于为什么在幕后被TypedReference?它是如此之快,安全......几乎是不可思议的!的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆