Autor Beitrag
C#
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 561
Erhaltene Danke: 65

Windows 10, Kubuntu, Android
Visual Studio 2017, C#, C++/CLI, C++/CX, C++, F#, R, Python
BeitragVerfasst: Fr 22.05.15 16:48 
Hey Leute,

bei meinem akltuellen Projekt versuche ich Methoden via Reflection dynamisch aufzurufen. Da ich die Signatur der entsprechenden Methoden nicht kenne, kann ich kein Delegate erstellen. Es kann aber passieren das eine Methode einige tausend Mal ausgeführt werden muss und dafür nicht viel Zeit benötigen darf (<1s). Wenn ich jetzt MehtodInfo.DynamicInvoke() benutze, bin ich deutlich zu langsam.
Weiß jemand wie man die Methoden schnell aufrufen kann?

Falls es hilfreiche ist: alle Methoden sind auf jeden Fall statisch, ich verwende C#6.0 und .NET 4.5 (oder höher).

_________________
Der längste Typ-Name im .NET-Framework ist: ListViewVirtualItemsSelectionRangeChangedEventHandler
Ralf Jansen
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 4700
Erhaltene Danke: 991


VS2010 Pro, VS2012 Pro, VS2013 Pro, VS2015 Pro, Delphi 7 Pro
BeitragVerfasst: Fr 22.05.15 17:54 
Eine erst zur Laufzeit bekannte Methode ganz oft aufrufen zu müssen klingt nach einer schrägen Anforderung. Mein Gefühl sagt mir das da ein Designfehler vorliegt oder das falsche Tool gewählt wurde.
Da ich das Problem trotzdem interessant finde würde ich vorher gerne das warum wissen bevor ich da Zeit investiere.

Edit: DynamicInvoke kenne ich nur von delegates und nicht von Methoden (da wärs einfach Invoke). Hast du das durcheinander gebracht oder ist das neu?
C# Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 561
Erhaltene Danke: 65

Windows 10, Kubuntu, Android
Visual Studio 2017, C#, C++/CLI, C++/CX, C++, F#, R, Python
BeitragVerfasst: Fr 22.05.15 18:45 
Zitat:
Hast du das durcheinander gebracht oder ist das neu?

Jop ich meine Delegates :mrgreen:

Hintergrund der ganzen Geschichte ist folgender:
Ich habe mir ein Funktionsplotter gebastelt, der 2D Graphen plottet. Aus einer Gleichung (z.B. f(x)=x+sin(x) oder h(v)=v^2-f(v)) wird dann ein Plot in einem festgelegten Bereich gemacht (Graphics oder Bitmap). Das ganze habe ich so gemacht, dass ich die Gleichung als string an einen selbstgeschriebenen Parser weitergegeben habe. Eine andere Klasse erzeugt daraus dann C# Code und kompiliert den mittels CodeDom. Aus dem erzeugten Assembly hole ich mir dann die Methode, die der Gleichung entspricht und erzeuge davon ein Delegate. Das geht da ja weil jede Methode die Gleiche Signatur hat:
ausblenden C#-Quelltext
1:
public static double f(double x)					

f und x ändern sich natürlich je nach Namen und Parameter der Funktionsgleichung.
Über das erzeugte Delegate konnte ich dann ganz fix die Methode innerhalb des Plotbereichs auswerten.

Jetzt habe ich mir gedacht ich kann das noch weiter spinnen und mache mir ein kleines Mathematik Tool (sozusagen Maple für Arme :mrgreen:). Dafür möchte ich aber auch mehrdimensionale Funktionen zulassen (z.B. g(x,y)=x*y) und Methoden die einfach mehrere Parameter benötogen (z.B. plot(Ausdruck, linke Grenze, rechte Grenze, obere Grenze, untere Grenze)). Da auch der User solche Funktionen definieren kann, kann ich unmöglich alle Signaturen kennen.

Deshalb möchte ich das mit DynamicInvoke machen.

Kann natürlich auch sein dass das schlechter Stil ist, ich weiß nicht wie dass bei professionellen Anwendungen gemacht wird.

Das ganze sieht bis jetzt so aus ("plot" ist momentan bekannte Methode und wird direkt aufgerufen, also nix mit Delegates und DynamicInvoke):
Bild
Einloggen, um Attachments anzusehen!
_________________
Der längste Typ-Name im .NET-Framework ist: ListViewVirtualItemsSelectionRangeChangedEventHandler
Ralf Jansen
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 4700
Erhaltene Danke: 991


VS2010 Pro, VS2012 Pro, VS2013 Pro, VS2015 Pro, Delphi 7 Pro
BeitragVerfasst: Fr 22.05.15 21:50 
Wenn MethodInfo.Invoke oder Delegate.DynamicInvoke zu langsam ist (hast du eigentlich beides ausprobiert?) weißt du um wieviel? Also wie hoch der Overhead ist?
Eine Idee wäre, da du eh schon mit dem CodeDom arbeitest, auch den Code der die Methoden aufruft, per CodeDom zu erzeugen. Diesem Code dann halt mit einem eindeutig aufrufbaren bekannten Interface versorgen so das dieses direkt nutzbar ist.

Für diesen Beitrag haben gedankt: C#
C# Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 561
Erhaltene Danke: 65

Windows 10, Kubuntu, Android
Visual Studio 2017, C#, C++/CLI, C++/CX, C++, F#, R, Python
BeitragVerfasst: Fr 22.05.15 22:58 
Oh man da hätte ich auch selber drauf kommen können.

Danke.

Ich habe gleich mal einen Performancetest gemacht. Hier ist der Output:
ausblenden Quelltext
1:
2:
3:
4:
5:
6:
100.000.000 calls

direct call:     2,61018s
direct invoke:   2,643238s
dynamic invoke:  33,7708481s
codedom compile: 4,3941933s


direct call ruft die Methode direkt, also hardcoded, auf.
direct invoke ruft die Methode über ein delegate mit bekannter Signatur auf.
dynamic invoke benutzt MethodInfo.Invoke().
codedom compile benutzt die von Ralf genannte Methode: es wird ein temporöres Assembly erzeugt dass über eine definierte Schnittstelle arbeitet (in meinem Fall is erst mal alles object)

Ich lasse den Thread hier mal auf "Kein Status", falls jemand noch eine andere Lösung hat.


// EDIT

Die Testmethode sieht so aus:
ausblenden C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
namespace Tests
{
    public static class Mathematics
    {
        public static double Compute(double x, double y)
        {
            return 2 * x - x * x + 10 - y;
        }
    }
}


Und ein Testcase sieht so aus:
ausblenden C#-Quelltext
1:
2:
3:
4:
5:
6:
watch.Start();
for (int i = 0; i < count; i++)
{
   a = Mathematics.Compute(i % 10d, i % 200d);
}
watch.Stop();

_________________
Der längste Typ-Name im .NET-Framework ist: ListViewVirtualItemsSelectionRangeChangedEventHandler
Ralf Jansen
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 4700
Erhaltene Danke: 991


VS2010 Pro, VS2012 Pro, VS2013 Pro, VS2015 Pro, Delphi 7 Pro
BeitragVerfasst: Sa 23.05.15 11:27 
Klasse das das funktioniert ;)

Aber woher kommt die Differenz zwischen direktem Aufruf und dem über CodeDom kompilierten Teil? Hätte vermutet das es der ~gleiche~ Code ist. Ist da der Kompilieraufwand reingerechnet?
C# Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 561
Erhaltene Danke: 65

Windows 10, Kubuntu, Android
Visual Studio 2017, C#, C++/CLI, C++/CX, C++, F#, R, Python
BeitragVerfasst: Sa 23.05.15 15:24 
Nein die Zeit fürs kompilieren ist nicht eingerechnet.

Ich habe dass mal weiter getestet:
ausblenden Quelltext
1:
2:
3:
4:
5:
6:
7:
100000000 calls

direct call:                          2,6199334s
direct invoke:                        2,6119872s
codedom Func<object[], object>:       4,3890145s
codedom Func<double[], double>:       3,1857938s
codedom Func<double, double, double>: 2,7391607s


Es liegt also daran, dass ich für die CodeDom Geschichte die Parameter in ein Array packen muss und dann auch noch boxing und unboxing dazukommt.
Der letzte Test hier verwendet genau die Signatur der Testmethode, läuft aber auch über CodeDom.

_________________
Der längste Typ-Name im .NET-Framework ist: ListViewVirtualItemsSelectionRangeChangedEventHandler