我创建了一个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倍。
答案 0 :(得分:2)
经过几个小时的调查后,我相信这个问题是由额外的装饰引起的。 VB给幕后的课程。
如果你在VB中查看一个简单的类,它比同等的C#类(例如Binder ... / Declared ...)还有更多属性等。这甚至适用于在VB中实例化的C#类。此外,性能分析器显示,在VB中反序列化/序列化ClaimsIdentity占用了相当大的时间。在C#中没有这个迹象。我再次猜测这是额外的装饰&#39;给了VB中的一个类。
检查了一下,看不到任何方法可以删除这些额外的东西。
所以我唯一的解决方案是在单独的C#dll中实现沙盒代码。