为什么在幕后被TypedReference?它是如此之快,安全......几乎是不可思议的! [英] Why is TypedReference behind the scenes? It's so fast and safe... almost magical!
问题描述
警告:这个问题有点异端宗教...程序员总是好的做法守法,请不要阅读。 :)
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:
- 就像任何功能,它的可能的成为路障新的功能,特别是因为这一个并不真正适合于C#的设计和要求怪异的语法扩展,并通过一类特殊处理运行。
- C#的所有实现必须以某种方式实现这个功能,它不一定琐碎/可能不上CLI之上运行在所有或者在没有可变参数一个CLI上运行的C#实现。
- 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.
- 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屋!