2017-01-23 5 views
3

Я создал изолированную среду AppDomain, чтобы пользователи могли запускать свой собственный код (C# или VB) из родительского приложения (написанного на VB). Я извлек основной код и создал два одинаковых приложения: один в VB один C#.Почему C# гораздо быстрее при вызове функции в AppDomain, чем VB

Я был поражен, обнаружив, что версия C# работает как минимум в 60 раз быстрее.

Я не могу найти ссылки на это поведение в StackOverflow или Google. Есть ли какая-то большая неэффективность в том, как VB сериализует вызов Invoke?

Здесь В.Б. код, который является типом выполняется:

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); 
     } 
    } 
} 

Здесь Б. И. Л. (Конструктор + Выполнить):

.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; 
      } 

При повторном измерении разности скорости ближе к 41 раз быстрее в C#.

+4

VB и C# почти (но не полностью) идентичны во многих отношениях под капотом. Нет причин ожидать каких-либо существенных нарушений производительности. Я подозреваю, что ваше «идентичное» приложение не так точно, как вы ожидаете. –

+7

Я был поражен, обнаружив, что сообщение не содержит кода. На этом этапе можно только догадываться о том, что вы сделали неправильно ... –

+1

Даже если ваше утверждение верно, вам нужно доказать это здесь, поместив некоторый код и сообщив, где _60 раз быстрее_, и другие могут обсудить об этом. –

ответ

2

После (довольно) нескольких часов, исследующих это, я считаю, что проблема вызвана дополнительным «украшением», которое VB дает классам за кулисами.

Если вы проверили простой класс в VB, он имеет еще несколько свойств и т. Д., Чем эквивалентный класс C# (например, Binder .../Declared ...). Это даже относится к классам C#, созданным в VB. Кроме того, анализатор производительности показал, что десериализация/сериализация ClaimsIdentity занимает большую долю времени в VB. Никаких признаков этого в C#. Опять же я предполагаю, что это дополнительное «украшение», данное классу в VB.

Проверено немного и не видит способа удалить этот лишний материал.

Так что мое единственное решение - реализовать код песочницы в отдельной C# dll.

+0

Это имеет смысл. - Среди прочего, у C# есть оптимизационный компилятор (если вы включите оптимизацию, которая по умолчанию включена в режиме Release), я думаю, что команда DirectX сделала некоторые тесты назад, что показало, что C# смог работать на уровне 97% от скорости эквивалентного C++. - Итак, вы определенно хотите использовать C# над VB.NET, если скорость важна. :-) – BrainSlugs83