Autor |
Beitrag |
C#
Beiträge: 561
Erhaltene Danke: 65
Windows 10, Kubuntu, Android
Visual Studio 2017, C#, C++/CLI, C++/CX, C++, F#, R, Python
|
Verfasst: 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
Beiträge: 4705
Erhaltene Danke: 991
VS2010 Pro, VS2012 Pro, VS2013 Pro, VS2015 Pro, Delphi 7 Pro
|
Verfasst: 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#
Beiträge: 561
Erhaltene Danke: 65
Windows 10, Kubuntu, Android
Visual Studio 2017, C#, C++/CLI, C++/CX, C++, F#, R, Python
|
Verfasst: Fr 22.05.15 18:45
Zitat: | Hast du das durcheinander gebracht oder ist das neu? |
Jop ich meine Delegates
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 ). 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):
Einloggen, um Attachments anzusehen!
_________________ Der längste Typ-Name im .NET-Framework ist: ListViewVirtualItemsSelectionRangeChangedEventHandler
|
|
Ralf Jansen
Beiträge: 4705
Erhaltene Danke: 991
VS2010 Pro, VS2012 Pro, VS2013 Pro, VS2015 Pro, Delphi 7 Pro
|
Verfasst: 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#
Beiträge: 561
Erhaltene Danke: 65
Windows 10, Kubuntu, Android
Visual Studio 2017, C#, C++/CLI, C++/CX, C++, F#, R, Python
|
Verfasst: 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(); |
_________________ Der längste Typ-Name im .NET-Framework ist: ListViewVirtualItemsSelectionRangeChangedEventHandler
|
|
Ralf Jansen
Beiträge: 4705
Erhaltene Danke: 991
VS2010 Pro, VS2012 Pro, VS2013 Pro, VS2015 Pro, Delphi 7 Pro
|
Verfasst: 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#
Beiträge: 561
Erhaltene Danke: 65
Windows 10, Kubuntu, Android
Visual Studio 2017, C#, C++/CLI, C++/CX, C++, F#, R, Python
|
Verfasst: 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.
_________________ Der längste Typ-Name im .NET-Framework ist: ListViewVirtualItemsSelectionRangeChangedEventHandler
|
|
|