为什么C#在AppDomain中调用函数要比VB快得多

时间:2017-01-23 17:21:49

标签: c# .net vb.net appdomain

我创建了一个AppDomain沙箱,允许用户从父应用程序(用VB编写)运行自己的代码(C#或VB)。我提取了基本代码并创建了两个相同的应用程序,一个在VB中,一个是C#。

我惊讶地发现C#版本的运行速度至少快了60倍。

我在StackOverflow或Google上找不到任何对此行为的引用。 VB序列化Invoke调用的方式是否存在一些主要的低效率?

以下是正在执行的类型的VB代码:

Imports System.Reflection

Namespace UserCode
  Namespace Runtime

    Public Class Execute
      Inherits MarshalByRefObject

      Private _MethodInfo As MethodInfo

      Sub New()

        _MethodInfo = Nothing

      End Sub

      Public Sub SetAssembly(assemblyName As String, functionName As String)

        _MethodInfo = Nothing

        If assemblyName <> "" Then

          Dim assembly As Assembly = AppDomain.CurrentDomain.Load(assemblyName)
          Dim type As Type = assembly.GetType("CompiledUserCode")

          _MethodInfo = type.GetMethod(functionName, BindingFlags.Public Or BindingFlags.Static)

        End If

      End Sub

      Public Function ExecuteFunction(args() As Object) As Object

        Return _MethodInfo.Invoke(Nothing, args)

      End Function

    End Class

  End Namespace
End Namespace

这是等效的C#

using System;
using System.Reflection;


namespace UserCode
{
   public class Execute:MarshalByRefObject
    {
        private MethodInfo _MethodInfo;

     public Execute()
      {
        _MethodInfo = null;

      }

      public void SetAssembly(string assemblyName ,string functionName)
      {
        _MethodInfo = null;

        if( assemblyName != "")
        {

         var assembly  = AppDomain.CurrentDomain.Load(assemblyName);
          var type = assembly.GetType("CompiledUserCode");

          _MethodInfo = type.GetMethod(functionName, BindingFlags.Public | BindingFlags.Static);

        }
      }

      public object ExecuteFunction(object[] args)
      {
          return _MethodInfo.Invoke(this, args);
      }
    }
}

这是VB IL(构造函数+执行):

.class public auto ansi UserCode.Runtime.Execute
    extends [mscorlib]System.MarshalByRefObject
{
    // Fields
    .field private class [mscorlib]System.Reflection.MethodInfo _MethodInfo

    // Methods
    .method public specialname rtspecialname 
        instance void .ctor () cil managed 
    {
        // Method begins at RVA 0x21c0
        // Code size 14 (0xe)
        .maxstack 8

        IL_0000: ldarg.0
        IL_0001: call instance void [mscorlib]System.MarshalByRefObject::.ctor()
        IL_0006: ldarg.0
        IL_0007: ldnull
        IL_0008: stfld class [mscorlib]System.Reflection.MethodInfo UserCode.Runtime.Execute::_MethodInfo
        IL_000d: ret
    } // end of method Execute::.ctor

.method public 
    instance object ExecuteFunction (
        object[] args
    ) cil managed 
{
    // Method begins at RVA 0x221c
    // Code size 14 (0xe)
    .maxstack 3
    .locals init (
        [0] object ExecuteFunction
    )

    IL_0000: ldarg.0
    IL_0001: ldfld class [mscorlib]System.Reflection.MethodInfo UserCode.Runtime.Execute::_MethodInfo
    IL_0006: ldnull
    IL_0007: ldarg.1
    IL_0008: callvirt instance object [mscorlib]System.Reflection.MethodBase::Invoke(object, object[])
    IL_000d: ret
} // end of method Execute::ExecuteFunction

这是C#IL:

.class public auto ansi beforefieldinit UserCode.Execute
    extends [mscorlib]System.MarshalByRefObject
{
    // Fields
    .field private class [mscorlib]System.Reflection.MethodInfo _MethodInfo

    // Methods
    .method public hidebysig specialname rtspecialname 
        instance void .ctor () cil managed 
    {
        // Method begins at RVA 0x275c
        // Code size 14 (0xe)
        .maxstack 8

        IL_0000: ldarg.0
        IL_0001: call instance void [mscorlib]System.MarshalByRefObject::.ctor()
        IL_0006: ldarg.0
        IL_0007: ldnull
        IL_0008: stfld class [mscorlib]System.Reflection.MethodInfo UserCode.Execute::_MethodInfo
        IL_000d: ret
    } // end of method Execute::.ctor

.method public hidebysig 
        instance object ExecuteFunction (
            object[] args
        ) cil managed 
    {
        // Method begins at RVA 0x27b4
        // Code size 14 (0xe)
        .maxstack 8

        IL_0000: ldarg.0
        IL_0001: ldfld class [mscorlib]System.Reflection.MethodInfo UserCode.Execute::_MethodInfo
        IL_0006: ldarg.0
        IL_0007: ldarg.1
        IL_0008: callvirt instance object [mscorlib]System.Reflection.MethodBase::Invoke(object, object[])
        IL_000d: ret
    } // end of method Execute::ExecuteFunction

我能看到的唯一显着差异是:

.maxstack 3
        .locals init (
            [0] object ExecuteFunction
        )

目前,如果我希望利用C#速度,我唯一的选择就是创建一个包含沙箱代码的独立C#程序集。 60次实际上估计不足。只需通过调用ExecuteFunction函数来测量它:

object[] objects = new object[] { 1.0, 1.0, 1.0 };

作为参数并改变object[0]以防止任何优化(100000循环)。

实际在沙箱中运行的代码非常简单:

public static double StressFactor(double useStress, double stress, double p)
           {
           return useStress*stress+p;
           }

重新测量时,C#中的速度差异接近41倍。

1 个答案:

答案 0 :(得分:2)

经过几个小时的调查后,我相信这个问题是由额外的装饰引起的。 VB给幕后的课程。

如果你在VB中查看一个简单的类,它比同等的C#类(例如Binder ... / Declared ...)还有更多属性等。这甚至适用于在VB中实例化的C#类。此外,性能分析器显示,在VB中反序列化/序列化ClaimsIdentity占用了相当大的时间。在C#中没有这个迹象。我再次猜测这是额外的装饰&#39;给了VB中的一个类。

检查了一下,看不到任何方法可以删除这些额外的东西。

所以我唯一的解决方案是在单独的C#dll中实现沙盒代码。