Entwickler-Ecke
Basistechnologien - Reflection - Schnelle Version von DynamicInvoke?
C# - Fr 22.05.15 16:48
Titel: Reflection - Schnelle Version von DynamicInvoke?
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).
Ralf Jansen - 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# - 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:
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):
Ralf Jansen - 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.
C# - 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:
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:
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:
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(); |
Ralf Jansen - 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# - Sa 23.05.15 15:24
Nein die Zeit fürs kompilieren ist nicht eingerechnet.
Ich habe dass mal weiter getestet:
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.
Entwickler-Ecke.de based on phpBB
Copyright 2002 - 2011 by Tino Teuber, Copyright 2011 - 2024 by Christian Stelzmann Alle Rechte vorbehalten.
Alle Beiträge stammen von dritten Personen und dürfen geltendes Recht nicht verletzen.
Entwickler-Ecke und die zugehörigen Webseiten distanzieren sich ausdrücklich von Fremdinhalten jeglicher Art!