Wenn dein Problem oder deine Frage geklärt worden ist, markiere den Beitrag als "Lösung",
indem du auf den "Lösung" Button rechts unter dem entsprechenden Beitrag klickst. Vielen Dank!
Folgende Problemstellung:
Ich habe ein Beispielcode des Herstellers einer Bibliothek genommen, in ein C++ Projekt gepackt und als exe kompiliert. Der Code arbeitet sauber und am wichtigsten sehr schnell. Danach kompiliere ich den gleichen Code als dll und der Code arbeitet immernoch sauber, aber um einiges langsamer. Ich weiß allerdings das es keinen unterschied in der Ausfürhungsgeschwindigkeit zwischen exe und dll geben dürfte. Wie kann ich also eine dll in der geschwindikeit optimieren. Ich arbeite mit VS 2008 und weiß das es bestimmte Compiler-Schalter gibt (auf Geschwindigkeit optimierter Code...), die aber allesamt versagen. Hat jemand ein ähnliches Problem lösen können, oder irgendwelche Ideen?
Danke, eure abrissbirne
27.04.2009, 08:01 (Dieser Beitrag wurde zuletzt bearbeitet: 27.04.2009 08:03 von rolfk.)
' schrieb:Folgende Problemstellung:
Ich habe ein Beispielcode des Herstellers einer Bibliothek genommen, in ein C++ Projekt gepackt und als exe kompiliert. Der Code arbeitet sauber und am wichtigsten sehr schnell. Danach kompiliere ich den gleichen Code als dll und der Code arbeitet immernoch sauber, aber um einiges langsamer. Ich weiß allerdings das es keinen unterschied in der Ausfürhungsgeschwindigkeit zwischen exe und dll geben dürfte. Wie kann ich also eine dll in der geschwindikeit optimieren. Ich arbeite mit VS 2008 und weiß das es bestimmte Compiler-Schalter gibt (auf Geschwindigkeit optimierter Code...), die aber allesamt versagen. Hat jemand ein ähnliches Problem lösen können, oder irgendwelche Ideen?
Danke, eure abrissbirne
Ohne den Code zu sehen lässt sich hier eigentlich gar nichts sagen. Das könnte an sehr viel Dingen liegen. Beispielsweise daran, dass ein C(++) Executable grundsätzlich single threaded ist, ausser Du machst Dir grosse Mühe im C Code selber.
Die DLL innerhalb von LabVIEW, läuft in einem Multithreaded System, da LabVIEW mit ziemlich vielen Threads aufgestartet wird. Wie wird denn die DLL Funktion aufgerufen? Innerhalb des UI Threads oder als Reentrant?
Wenn Du sie im UI Thread laufen lässt muss sich der DLL Code die CPU mit dem ganzen LabVIEW UI System teilen, der ausser zum Updaten des User Interfaces auch für allerlei Synchronisationsaufgaben zwischen verschiedenen beschützten Subsystemen verantwortlich ist.
Aber man kann nicht einfach eine Funktion reentrant konfigurieren wenn die nicht auch explizit dafür programmiert wurde. Dazu sollte sie grundsätzlich keinerlei globale Variablen ansprechen und auch nicht ohne Schutz (Semaphore oder ähnliches) auf externe Resourcen wie Hardwareschnittstellen etc zugreifen.
Aber selbst wenn Du die DLL korrekt reentrant ausführst wird sich der entsprechende Thread die CPU mit anderen Threads innerhalb von LabVIEW teilen müssen und wird wahrscheinlich ein wenig langsamer laufen als in einem single threaded Executable.
Ich habe den eigentlichen Quellcode in C++ Multithreaded programmiert. Maximal laufen bis zu drei Threads. Diese werden aber durch Semaphoren synchronisiert. Ich greife natürlich auch auf eine Hardware zu. Dafür habe ich Critical Sections verwandt um einen gleichzeitigen Zugriff zu vermeiden. Allerdings verwende ich schon globale Variablen die sich aber auch innerhalb der Critical Section befinden. Ist dies ausreichend um ein Skript als Threadsafe zu bezeichnen? Und sollte ich den reentrant Aufruf dann mal versuchen?
Danke für deine guten Antworten Rolf.
27.04.2009, 09:48 (Dieser Beitrag wurde zuletzt bearbeitet: 27.04.2009 09:50 von rolfk.)
' schrieb:Ich habe den eigentlichen Quellcode in C++ Multithreaded programmiert. Maximal laufen bis zu drei Threads. Diese werden aber durch Semaphoren synchronisiert. Ich greife natürlich auch auf eine Hardware zu. Dafür habe ich Critical Sections verwandt um einen gleichzeitigen Zugriff zu vermeiden. Allerdings verwende ich schon globale Variablen die sich aber auch innerhalb der Critical Section befinden. Ist dies ausreichend um ein Skript als Threadsafe zu bezeichnen? Und sollte ich den reentrant Aufruf dann mal versuchen?
Danke für deine guten Antworten Rolf.
Solange alle globalen Resourcen (Hardware, globale Variablen, etc) innerhalb von Critical Sections geschützt sind sollte der Code als wiedereintrittsfähig (reentrant) gelten können. Ob das aber hilft mit der Performance ist eine andere Frage.
Critical Sections haben auch die Möglichkeit um Probleme zu verursachen, wie Priority inversion, interlocking, usw. Damit kann Code der grundsätzlich schnell läuft plötzlich sehr langsam laufen oder sich überhapt komplett blockieren und den entsprechenden Thread völlig aufhängen. Das hässliche dabei ist dass diese Problem sehr schwierig zu debuggen sind, da nicht immer genau und einfach reproduzierbar und im Moment wo Du im Locking zurecht kommst eigentlich auch nicht mehr debugbar sind, da der Debugger selber im Suspended Mode arbeiten können muss um Dir Dinge wie Single-Stepping und Source Code Level Debugging möglich zu machen.
So, ich habe meinen Übeltäter glaube ich ausfindig gemacht. Es liegt nicht an der DLL selbst, sondern an dem Quellcode. Den sollte ich optimieren. Ich denke das wird auch der häufigste Grund sein, wesshalb eine DLL nicht so schnell arbeitet.
Ich hoffe es stört niemanden wenn ich an dieser Stelle mein Problem weiter vertiefe.
Ich habe wie gesagt eine Datenaufnahme mit einer C-Bibliothek geschrieben. Das eigentliche Herzstück ist die Processingfunktion, welche die Daten aufnimmt. Dies soll in Echtzeit geschehen. Dazu werden Speicher mit Funktionen der Bibliothek reserviert. Ich dachte mir das folgendermaßen:
Nach jedem Schuss (der Datenaufnahme) wird eine Funktion aufgerufen. In dieser Funktion möchte ich die aufgenommenen Daten nur an LabVIEW weiterleiten. Dort werden die aufgenommenen Daten in eine Queue geschoben und weiterverarbeitet. Soweit funtkioniert das auch. Allerdings macht mir die Funktion, welche mir die Daten in ein LabVIEW Array kopiert, einen strich durch die Rechnung. Diese ist nämlich zu langsam. Ich kann mir allerdings die Adresse des Puffers auslesen, in welchen die Daten abgelegt wurden. Jetzt war mein zweiter Gedanke ich übergebe diesen Pointer einfach der Funktion PostLVUserEvent, aber das führt zu einem Fehler (unexpected exception oder so ähnlich). Nun habe ich versucht mittels des MoveBlock die Daten in mein LVPuffer zu schieben. Das funktioniert noch, aber wenn ich dann dies versuche:
<div class='codetop'>CODE</div><div class='codemain' style='height:200px;white-space:pre;overflow:auto'>(*Memory)->dimSize[0] = 1234;
(*Memory)->dimSize[1] = 1234;</div>
um es mit dem PostLVUserEvent an LV zu übergeben kommt selbe Fehlermeldung wie eben geschildert. In etwa sieht das ganze so aus:
<div class='codetop'>CODE</div><div class='codemain' style='height:200px;white-space:pre;overflow:auto'>typedef struct{
int32 dimSize[2];
uInt16 elements;
} **UInt16HAndle;
UInt16Handle LVMemory = NULL;
// Variable zum Speichern der Pufferadresse.
unsigned long addr;
GetBufferInfo(EventID, MODIFIED_BUFFER_ID, &Buffer);
BufferInquire(Buffer, HOST_ADDRESS, &addr);
MoveBlock((void*)addr, (void*)LVMemory, sizeof(uInt16)*SizeX*SizeY);
(*LVMemory)->dimSize[0] = SizeY;
(*LVMemory)->dimSize[1} = SizeX;
PostLVUserEvent(LVUserEventRef, &LVMemory);</div>
Wie kann ich im Letzten Schritt die Daten an mein LV Array übergeben und das möglichst schnell.
' schrieb:So, ich habe meinen Übeltäter glaube ich ausfindig gemacht. Es liegt nicht an der DLL selbst, sondern an dem Quellcode. Den sollte ich optimieren. Ich denke das wird auch der häufigste Grund sein, wesshalb eine DLL nicht so schnell arbeitet.
Ich hoffe es stört niemanden wenn ich an dieser Stelle mein Problem weiter vertiefe.
Ich habe wie gesagt eine Datenaufnahme mit einer C-Bibliothek geschrieben. Das eigentliche Herzstück ist die Processingfunktion, welche die Daten aufnimmt. Dies soll in Echtzeit geschehen. Dazu werden Speicher mit Funktionen der Bibliothek reserviert. Ich dachte mir das folgendermaßen:
Nach jedem Schuss (der Datenaufnahme) wird eine Funktion aufgerufen. In dieser Funktion möchte ich die aufgenommenen Daten nur an LabVIEW weiterleiten. Dort werden die aufgenommenen Daten in eine Queue geschoben und weiterverarbeitet. Soweit funtkioniert das auch. Allerdings macht mir die Funktion, welche mir die Daten in ein LabVIEW Array kopiert, einen strich durch die Rechnung. Diese ist nämlich zu langsam. Ich kann mir allerdings die Adresse des Puffers auslesen, in welchen die Daten abgelegt wurden. Jetzt war mein zweiter Gedanke ich übergebe diesen Pointer einfach der Funktion PostLVUserEvent, aber das führt zu einem Fehler (unexpected exception oder so ähnlich). Nun habe ich versucht mittels des MoveBlock die Daten in mein LVPuffer zu schieben. Das funktioniert noch, aber wenn ich dann dies versuche:
<div class='codetop'>CODE</div><div class='codemain' style='height:200px;white-space:pre;overflow:auto'>(*Memory)->dimSize[0] = 1234;
(*Memory)->dimSize[1] = 1234;</div>
um es mit dem PostLVUserEvent an LV zu übergeben kommt selbe Fehlermeldung wie eben geschildert. In etwa sieht das ganze so aus:
<div class='codetop'>CODE</div><div class='codemain' style='height:200px;white-space:pre;overflow:auto'>typedef struct{
int32 dimSize[2];
uInt16 elements;
} **UInt16HAndle;
UInt16Handle LVMemory = NULL;
// Variable zum Speichern der Pufferadresse.
unsigned long addr;
GetBufferInfo(EventID, MODIFIED_BUFFER_ID, &Buffer);
BufferInquire(Buffer, HOST_ADDRESS, &addr);
MoveBlock((void*)addr, (void*)LVMemory, sizeof(uInt16)*SizeX*SizeY);
(*LVMemory)->dimSize[0] = SizeY;
(*LVMemory)->dimSize[1} = SizeX;
PostLVUserEvent(LVUserEventRef, &LVMemory);</div>
Wie kann ich im Letzten Schritt die Daten an mein LV Array übergeben und das möglichst schnell.
Egal, sorry, aber das NumericArrayResize hab ich natürlich in meinem Quellcode. Dies wied nur an einer anderen Stelle gemacht und ich hab vergessen es in diese Codebox zu kopieren. Das Thema hatten wir ja schonmal ;-) Also kein Nullhandle, aber ich hab vergessen auf (*LVMemory)->elemets zu dereferenzieren. Das werd ich morgen gleich mal testen.
Gibt es eventuell auch noch einen schnelleren Weg? Oder kann ich sogar den Pointer des Originalpuffers irgendwie verwenden? Ich allokiere den Puffer momentan auf der Aufnahmekarte, kann ihn aber auch woanders allokieren.
Danke^_^
29.04.2009, 19:01 (Dieser Beitrag wurde zuletzt bearbeitet: 29.04.2009 19:02 von jg.)
' schrieb:Warum kann ich eigenltich nicht mehr zitieren?
Kannst du schon noch, bloß wird inzwischen aktive Mitarbeit vom Nutzer gefordert.
Einfach den Button "Zitieren" unter einem (oder mehreren) Beitrag(en) aktivieren, und dann erst auf "Antworten" gehen.
Hintergrund: Es wurde in letzter Zeit viel zu häufig grundlos zitiert durch den nicht mehr vorhandenen Antwort-Button direkt unter einem Beitrag. Das hat viele Threads schlecht lesbar und unnötig lang gemacht.
Gruß, Jens
Wer die erhabene Weisheit der Mathematik tadelt, nährt sich von Verwirrung. (Leonardo da Vinci)
!! BITTE !! stellt mir keine Fragen über PM, dafür ist das Forum da - andere haben vielleicht auch Interesse an der Antwort!
' schrieb:Gibt es eventuell auch noch einen schnelleren Weg? Oder kann ich sogar den Pointer des Originalpuffers irgendwie verwenden? Ich allokiere den Puffer momentan auf der Aufnahmekarte, kann ihn aber auch woanders allokieren.
Nun, wenn Du die Möglichkeit hast Deiner C Routine einen Pointer vom Heap zu übergeben dann ginge es. Dazu musst Du natürlich das Handle in der richtigen Grösse mit NumericArrayResize() allozieren und dann das (*LVMemory)->elements als Heappointer an Deine Libraryfunktion übergeben. Aber die Libraryfunktion muss natürlich als DLL von Dir aufgerufen werden und muss diesen Heappointer auch im Context der LabVIEW Applikation ansprechen. Das verlangt von der Library einiges and komplizierter Arbeit und deshalb wird das meist nicht unterstützt!
Ansonsten ist das kopieren der Daten unabwendbar. Die PostLVUserEvent() Funktion kann aus logischen Gründen nur mit LabVIEW native Datentypen arbeiten.
' schrieb:Ansonsten ist das kopieren der Daten unabwendbar. Die PostLVUserEvent() Funktion kann aus logischen Gründen nur mit LabVIEW native Datentypen arbeiten.
Rolf Kalbermatter
Ok, danke dafür.
Es funktioniert zwar, ist aber auch nicht wesentlich schneller. Gibt es keine Möglichkeit einen Pointer auf Daten direkt an LV zu übergeben? Die Daten die aufgenommen werden haben den Datentyp Unsigned+16. Oder kann man sogar anstallt PostLVUserEvent Daten direkt in die LV Queu schieben.