2017-01-23 1 views
3

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#.

+4

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. –

+7

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 ... –

+1

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. –

Antwort

2

Nach (ziemlich) ein paar Stunden, die dies untersuchen, glaube ich, dass das Problem durch die zusätzliche "Dekoration" verursacht wird, die VB Klassen hinter den Kulissen gibt.

Wenn Sie eine einfache Klasse in VB auschecken, hat sie ziemlich viele Eigenschaften usw. als die entsprechende C# -Klasse (zB Binder .../Deklariert ...). Dies gilt sogar für in VB instantiierte C# -Klassen. Darüber hinaus zeigte der Performance Analyzer, dass die Deserialisierung/Serialisierung von ClaimsIdentity einen erheblichen Teil der Zeit in VB beansprucht. Kein Anzeichen dafür in C#. Ich vermute mal, dass dies die zusätzliche "Dekoration" ist, die einer Klasse in VB gegeben wird.

Checked um ein bisschen und kann keine Möglichkeit sehen, diese zusätzlichen Sachen zu entfernen.

So ist meine einzige Lösung, den Sandbox-Code in einer separaten C# dll zu implementieren.

+0

Das macht Sinn - C# hat unter anderem einen optimierenden Compiler (wenn man Optimierungen aktiviert, was im Release-Modus der Standard ist), denke ich, dass das DirectX-Team einige Benchmarks gemacht hat zurück, die gezeigt haben, dass C# mit 97% der Geschwindigkeit des entsprechenden C++ laufen konnte. - Also, wenn C# Geschwindigkeit ist, solltest du definitiv C# über VB.NET benutzen :-) – BrainSlugs83

Verwandte Themen