Ich habe eine Sandbox für die Anwendungsdomäne erstellt, damit Benutzer ihren eigenen Code (C# oder VB) von einer übergeordneten Anwendung (in VB geschrieben) ausführen können. Ich extrahierte den wesentlichen Code und erstellte zwei identische Anwendungen, eine in VB one C#.Warum ist C# beim Aufrufen einer Funktion in einer Anwendungsdomäne viel schneller als VB
Ich war erstaunt zu finden, dass die C# Version mindestens 60 mal schneller läuft.
Ich kann keine Hinweise auf dieses Verhalten auf StackOverflow oder Google finden. Gibt es eine große Ineffizienz in der Art, wie VB den Invoke
Aufruf serialisiert? Hier
ist die VB-Code, der den Typ ausgeführt wird:
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
Hier ist das Äquivalent 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);
}
}
}
Hier ist die VB IL (Constructor + Execute):
.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
Hier ist die 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
Der einzige wesentliche Unterschied, den ich sehen kann, ist:
.maxstack 3
.locals init (
[0] object ExecuteFunction
)
Derzeit wenn ich will die Vorteile der C# Geschwindigkeit nehmen meine einzige Möglichkeit ist, eine separate C# Montag mit dem Sandbox-Code zu erstellen. Das 60-fache ist tatsächlich eine Unterschätzung. Es wird gemessen, indem einfach die Funktion mit ExecuteFunction
Aufruf:
object[] objects = new object[] { 1.0, 1.0, 1.0 };
als Argumente und object[0]
Variieren alle Optimierungen (100000 Schleifen) zu verhindern.
Der Code, der in der Sandbox tatsächlich ausgefuehrt wird ist sehr einfach:
public static double StressFactor(double useStress, double stress, double p)
{
return useStress*stress+p;
}
Auf Wieder Messung der Geschwindigkeitsdifferenz ist näher an 41-mal schneller in C#.
VB und C# sind fast (aber nicht vollständig) in vielerlei Hinsicht unter der Haube identisch. Kein Grund, wesentliche Leistungsabweichungen zu erwarten. Ich vermute, dass Ihre "identische" Anwendung nicht so identisch ist, wie Sie es erwarten. –
Ich war erstaunt zu finden, dass Post keinen Code enthält. An dieser Stelle kann man nur Vermutungen darüber äußern, was Sie falsch gemacht haben ... –
Auch wenn deine Aussage korrekt ist, musst du sie hier beweisen, indem du Code eingibst und erzählst, woher _60-mal schneller_ kommt, damit andere darüber diskutieren können. –