简体   繁体   English

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

[英]Why is C# much faster at calling a function in an AppDomain than VB

I have created an AppDomain sandbox to allow users to run their own code (C# or VB) from a parent application (written in VB). 我创建了一个AppDomain沙箱,允许用户从父应用程序(用VB编写)运行自己的代码(C#或VB)。 I extracted the essential code and created two identical applications, one in VB one C#. 我提取了基本代码并创建了两个相同的应用程序,一个在VB中,一个是C#。

I was astonished to find that the C# version runs at least 60 times faster. 我惊讶地发现C#版本的运行速度至少快了60倍。

I cannot find any references to this behaviour on StackOverflow or Google. 我在StackOverflow或Google上找不到任何对此行为的引用。 Is there some major inefficiency in the way VB serialises the Invoke call? VB序列化Invoke调用的方式是否存在一些主要的低效率?

Here is the VB code that is the type being executed: 这是正在执行的类型的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

Here is the equivalent C# 这是等效的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);
      }
    }
}

Here is the VB IL (Constructor + Execute): 这是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

Here is the C# IL: 这是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

The only significant difference I can see is: 我能看到的唯一显着差异是:

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

At present if I wish to take advantage of the C# speed my only option is to create a separate C# assembly containing the sandbox code. 目前,如果我希望利用C#速度,我唯一的选择是创建一个包含沙盒代码的单独C#程序集。 The 60 times is actually an under-estimate. 60次实际上估计不足。 It is measured simply by calling the ExecuteFunction function with: 只需通过调用ExecuteFunction函数来测量它:

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

as the arguments and varying object[0] to prevent any optimisations (100000 loops). 作为参数和变化object[0]以防止任何优化(100000循环)。

The code that actually gets run in the sandbox is very simple: 实际在沙箱中运行的代码非常简单:

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

On re-measuring the speed difference is closer to 41 times faster in C#. 在重新测量时,C#中的速度差异接近41倍。

After (quite) a few hours investigating this I believe that the issue is caused by the extra 'decoration' that VB gives to classes behind the scenes. 经过几个小时的调查,我相信这个问题是由VB给予幕后课程的额外“装饰”造成的。

If you check out a simple class in VB it has quite a few more properties etc than the equivalent C# class (eg Binder... / Declared...). 如果你在VB中查看一个简单的类,它比同等的C#类(例如Binder ... / Declared ...)有更多的属性等。 This even applies to C# classes instantiated in VB. 这甚至适用于在VB中实例化的C#类。 In addition the performance analyzer showed that deserializing/serializing ClaimsIdentity was taking a goodly proportion of the time in VB. 此外,性能分析器显示,在VB中反序列化/序列化ClaimsIdentity占用了相当大的时间。 No sign of this in C#. 在C#中没有这个迹象。 Again I am guessing this is the extra 'decoration' given to a class in VB. 我再次猜测这是给VB中一个类的额外“装饰”。

Checked around a bit and can see no way to remove this extra stuff. 检查了一下,可以看到没有办法删除这些额外的东西。

So my only solution is to implement the sandbox code in a separate C# dll. 所以我唯一的解决方案是在单独的C#dll中实现沙盒代码。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM