Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/275.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/5/ember.js/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 为什么添加beforefieldinit可以显著提高泛型类的执行速度?_C#_Cil_Reflection.emit_Il - Fatal编程技术网

C# 为什么添加beforefieldinit可以显著提高泛型类的执行速度?

C# 为什么添加beforefieldinit可以显著提高泛型类的执行速度?,c#,cil,reflection.emit,il,C#,Cil,Reflection.emit,Il,我正在处理一个代理,对于带有引用类型参数的泛型类,速度非常慢。特别是对于泛型方法(对于仅返回null的普通泛型方法,大约为400 ms,而对于3200 ms)。我决定试着看看如果我用C#重写生成的类,它的性能会如何,它的性能要好得多,与我的非泛型类代码的性能差不多 这是我写的C#类::(注意,我通过命名方案进行了更改,但不是很多):: namespace TestData { 公共类TestClassProxy:TestClass { 私有调用处理程序_0_测试; 私有调用处理程序1_测试; 私

我正在处理一个代理,对于带有引用类型参数的泛型类,速度非常慢。特别是对于泛型方法(对于仅返回null的普通泛型方法,大约为400 ms,而对于3200 ms)。我决定试着看看如果我用C#重写生成的类,它的性能会如何,它的性能要好得多,与我的非泛型类代码的性能差不多

这是我写的C#类::(注意,我通过命名方案进行了更改,但不是很多)::

namespace TestData
{
公共类TestClassProxy:TestClass
{
私有调用处理程序_0_测试;
私有调用处理程序1_测试;
私有静态只读InvocationHandler[]\u proxy\u handlers=新InvocationHandler[]{
新的调用处理程序(新的Func(TestClassProxy.s_0_Test)),
新的GenericInvocationHandler(typeof(TestClassProxy),“s_1_Test”);
公共TestClassProxy(调用处理程序[]处理程序)
{
if(handlers==null)
{
抛出新的ArgumentNullException(“处理程序”);
}
if(handlers.Length!=2)
{
抛出新的ArgumentException(“处理程序需要是2个参数的数组。”,“处理程序”);
}
这个._0_Test=(InvocationHandler)(处理程序[0]?_proxy_处理程序[0]);
这个._1_Test=(调用处理程序)(处理程序[1]??\u代理处理程序[1]);
}
私有对象_0__测试()
{
返回base.Test();
}
私有对象_1__测试(pR local1),其中T:IConvertible
{
返回base.Test(local1);
}
公共静态对象s_0_测试(TestClass class1)
{
返回((TestClassProxy)class1)。\uuu0\uuuuu Test();
}
公共静态对象s_1_测试(TestClass class1,pR local1),其中T:IConvertible
{
返回((TestClassProxy)class1)。\uuuuu1\uuuuu测试(local1);
}
公共重写对象测试()
{
返回此。_0_测试目标(此);
}
公共覆盖对象测试(pR local1)
{
返回此._1_Test.Target(this,local1,GenericToken.Token);
}
}
}
这是在发布模式下编译到与我生成的代理相同的IL的,这里是代理的类::

namespace TestData
{
    public class TestClass<R>
    {
        public virtual object Test()
        {
            return default(object);
        }

        public virtual object Test<T>(R r) where T:IConvertible
        {
            return default(object);
        }
    }
}
namespace TestData
{
公共类TestClass
{
公共虚拟对象测试()
{
返回默认值(对象);
}
公共虚拟对象测试(R),其中T:IConvertible
{
返回默认值(对象);
}
}
}
有一个例外,我没有对生成的类型设置beforefieldinit属性。我刚刚设置了以下属性::public auto ansi

为什么使用beforefieldinit会使性能提高这么多

(唯一的另一个区别是我没有命名我的参数,这些参数在总体方案中并不重要。 方法和字段的名称被置乱以避免与实际方法冲突。 GenericToken和InvocationHandler是与参数无关的实现细节。
GenericToken实际上只是一个类型化的数据持有者,因为它允许我向处理程序发送“T”

InvocationHandler只是委托字段目标的持有者,没有实际的实现细节

GenericInvocationHandler使用调用站点技术(如DLR)根据需要重写委托以处理传递的不同泛型参数 )

编辑:: 这是测试线束::

private static void RunTests(int count = 1 << 24, bool displayResults = true)
{
    var tests = Array.FindAll(Tests, t => t != null);
    var maxLength = tests.Select(x => GetMethodName(x.Method).Length).Max();

    for (int j = 0; j < tests.Length; j++)
    {
        var action = tests[j];
        Stopwatch sw = Stopwatch.StartNew();
        for (int i = 0; i < count; i++)
        {
            action();
        }
        sw.Stop();
        if (displayResults)
        {
            Console.WriteLine("{2}  {0}: {1}ms", GetMethodName(action.Method).PadRight(maxLength),
                              ((int)sw.ElapsedMilliseconds).ToString(), j);
        }
        GC.Collect();
        GC.WaitForPendingFinalizers();
        GC.Collect();
    }
}

private static string GetMethodName(MethodInfo method)
{
    return method.IsGenericMethod
            ? string.Format(@"{0}<{1}>", method.Name, string.Join<Type>(",", method.GetGenericArguments()))
            : method.Name;
}
private static void运行测试(int count=1 t!=null);
var maxLength=tests.Select(x=>GetMethodName(x.Method).Length.Max();
对于(int j=0;j
在一次测试中,我做了以下几点:

Tests[0] = () => proxiedTestClass.Test();
Tests[1] = () => proxiedTestClass.Test<string>("2");
Tests[2] = () => handClass.Test();
Tests[3] = () => handClass.Test<string>("2");
RunTests(100, false);
RunTests();
Tests[0]=()=>proxiedTestClass.Test();
测试[1]=()=>proxiedTestClass.Test(“2”);
Tests[2]=()=>handClass.Test();
测试[3]=()=>handClass.Test(“2”);
运行测试(100,错误);
运行测试();
其中Tests是一个
Func[20]
proxiedTestClass
是我的程序集生成的类,
handClass
是我手工生成的类。 RunTests被调用两次,一次用于“预热”,另一次用于运行它并打印到屏幕上。我主要是从Jon Skeet的一篇文章中获取这段代码的

如第一部分第8.9.5节所述:

触发此类执行的时间和内容的语义 初始化方法,如下所示:

  • 类型可以有类型初始值设定项方法,也可以没有
  • 一个类型可以被指定为具有其类型初始值设定项方法的放松语义(为了下面的方便,我们称之为放松语义) 语义在fieldinit之前
  • 如果将标记为BeforeFieldInit,则该类型的初始值设定项方法将在首次访问任何静态字段时或之前执行 为该类型定义的
  • 如果未在ForeFieldInit之前标记,则该类型的初始值设定项方法将在处执行(即,通过以下方式触发):

    a。首次访问该类型的任何静态字段,或

    b。第一次调用
    Tests[0] = () => proxiedTestClass.Test();
    Tests[1] = () => proxiedTestClass.Test<string>("2");
    Tests[2] = () => handClass.Test();
    Tests[3] = () => handClass.Test<string>("2");
    RunTests(100, false);
    RunTests();
    
    public static object s_1_Test<T>(TestClass<pR> class1, pR local1) where T:IConvertible
    {
        return null;
    }
    
    00000000  xor         eax,eax
    00000002  ret
    
    00000000  sub         rsp,28h
    00000004  mov         rdx,rcx
    00000007  xor         ecx,ecx
    00000009  call        000000005F8213A0
    0000000e  xor         eax,eax
    00000010  add         rsp,28h
    00000014  ret