DLL & Speicherauslastung
-
Hallo,
letzte Woche habe ich ja das Dll-Tutorial durchgearbeitet und mit ein bißchen Forums-Hilfe meine erste dynamische DLL erstellt, deren Funktion in einer kleinen Testanwendung genutzt wird. Nun hatte ich gerade wieder etwas Zeit und habe mir mal angeschaut, was bei Prog.Nutzung (Testanwendung) im TaskManager so passiert...
Erstmal ne ProgBeschreibung: Das Prog hat einfach zwei Funktionen. Auf Knopfdruck wird eine Schleife gestartet, die einen Zähler von 0 bis 99 durchzählt und in einem Label die Zählschritte darstellt. Damit man die Änderung verfolgen kann, wird nach jedem Zählschritt ein delay eingefügt. Es gibt zwei Buttons um den Zähler zu starten. Bei dem 1. wird ein delay von 50ms direkt in der Schleife erzeugt, bei dem 2. wird das delay von 100ms über den Aufruf einer Dll-Funktion in der Schleife erreicht. Dies geschieht indirekt über eine Import-Datei.
Nun das Problem: Starte ich das Prog (Testanwendung), steht im Taskmanager unter 'Prozesse' die Speicherauslastung dafür bei ca. 5000k bis 5500k (warum variiert das? und ist das nicht ein bißchen viel?). Starte ich jetzt Button 1, verändert sich die Speicherauslastung nur geringfügig. Drücke ich Btn 1 ein zweites Mal, ändert sich die Speicherauslastung nicht mehr.
Starte ich nun Button 2, steigt die Speicherauslastung um ca. 200k bis 300k, und das jedes Mal wenn ich Button 2 drücke. Das kann doch irgendwie nicht gut sein oder? Was passiert denn erst bei umfangreichen Aufrufen v. Dll-Funktionen? Oder liest sich das, als ob ich mit meiner Dll was falsch mache?
Hier mal die Testanwendung://--------------------------------------------------------------------------- #include <vcl.h> #pragma hdrstop #include "Anwendg.h" #include "Import.h" //--------------------------------------------------------------------------- #pragma package(smart_init) #pragma resource "*.dfm" TForm1 *Form1; //--------------------------------------------------------------------------- __fastcall TForm1::TForm1(TComponent* Owner) : TForm(Owner) { } //--------------------------------------------------------------------------- void __fastcall TForm1::Button1Click(TObject *Sender) { Button1->Enabled= false; Button2->Enabled= false; for (int i=1; i<100; i++) { if (i<10) { Label1->Caption= "0" + IntToStr(int(i)); } else if (i>9) { Label1->Caption= IntToStr(int(i)); } Sleep(50); //delay in Schleife erzeugt Application->ProcessMessages(); } Label1->Caption= "00"; Button1->Enabled= true; Button2->Enabled= true; } //--------------------------------------------------------------------------- void __fastcall TForm1::Button2Click(TObject *Sender) { Button1->Enabled= false; Button2->Enabled= false; for (int i=1; i<100; i++) { if (i<10) { Label1->Caption= "0" + IntToStr(int(i)); } else if (i>9) { Label1->Caption= IntToStr(int(i)); } ImpWait(100); } // delay durch Aufruf einer Dll-Funktion Label1->Caption= "00"; Button1->Enabled= true; Button2->Enabled= true; } //---------------------------------------------------------------------------Habt Ihr dazu Hinweise für mich? Ich war der Meinung eine dynamische Dll wird bei Bedarf geladen und danach wieder entladen...

MfG
-
Wie lädst du denn deine dll, statisch oder dynamisch? Was genau passiert denn in ImpWait()?
-
Hey, Danke für Dein Interesse @Braunstein,
ich schrieb:
...meine erste dynamische DLL erstellt...
dynamisch also.
Braunstein schrieb:
Was genau passiert denn in ImpWait()?
Das hier:
Sleep(W); Application->ProcessMessages();Das geht über den Zwischenschritt einer Import-Datei, welche die Dll-Fkt. aufruft - wie im Tutorial beschrieben.
Ansonsten gibts die Vorgeschichte(Import-Datei, dDll, Anwendg) der Problematik auch hier.
Wie nun weiter?
-
Hallo,
Exakt kann ich dir nicht sagen was hier los ist. Ich vermute aber, dass es am ständigen laden/Entladen deiner dll liegt.
Versuche doch mal das Laden der dll zu zentralisieren. Z.Bsp. kannst du die dll im Konstruktor deiner Form laden und dir dort den Funktionszeiger holen (hddl merken). Im Destruktor kannst du dann FreeLibrary aufrufen.
Noch schöner fände ich das Laden/Entladen in einer Klasse zu kapseln, die dann einfach in deiner Anwendung instanziiert wird.
Sowas hierclass WaitClass { private: HINSTANCE hdll; typedef void TWait(int W); public: TWait* wait(); WaitClass() { hdll = LoadLibrary("Project2.dll"); if( !hdll ) throw std::logic_error("Cannot load Project2.dll"); wait = reinterpret_cast<TWait*>(GetProcAddress(hdll, "_Wait")); if( !wait ) throw std::logic_error("Cannot load Wait from dll"); } ~WaitClass() { FreeLibrary(hdll); } }
-
Hallo
steht im Taskmanager unter 'Prozesse' die Speicherauslastung dafür bei ca. 5000k bis 5500k (warum variiert das? und ist das nicht ein bißchen viel?).
Programme aus komplexen Frameworks wie die VCL sind nicht gerade sparsam mit Speicherauslastung. Allerdings ist davon bereits ein großer Teil der VCL-Kern, der nur einmalig vorhanden ist. Fügst du also neue Formulare hinzu wird sich der Verbrauch nur vergleichsweise wenig erhöhen.
Wenn dir das für dein Projekt zu viel ist must du in reiner WinAPI-Umgebung programmieren.dynamisch also.
Ich seh aber in deinem Quellcodeausszug kein LoadLibrary/GetProcAdress.
Denn dann würde ich darauf verwetten das du bei jedem Button-Aufruf die Library neu lädst aber nach Gebrauch nicht wieder entlädst. Aber wie gesagt ist bei dir gar nicht zu sehen, und so vermute ich wie Braunstein das du deine DLL eher statisch linkst./Edit : Ja ich bin langsam
bis bald
akari
-
Ich hab mich in meinem 2. Post etwas unverständlich ausgedrückt
:ich schrieb:
Ansonsten gibts die Vorgeschichte(Import-Datei, dDll, Anwendg) der Problematik auch hier.
Der Link unter "hier" führt zum gesamten Quellcode der Projektgruppe.
Braunstein schrieb:
Versuche doch mal das Laden der dll zu zentralisieren. Z.Bsp. kannst du die dll im Konstruktor deiner Form laden und dir dort den Funktionszeiger holen (hddl merken). Im Destruktor kannst du dann FreeLibrary aufrufen.
Das mach' ich doch, oder? (siehe Link "hier" im 2. Post von mir)
akari schrieb:
...so vermute ich wie Braunstein das du deine DLL eher statisch linkst.
Die ist doch dynamisch gelinkt, oder? (siehe Link "hier" im 2. Post von mir)
Ich hab' jetzt noch n Test gemacht: 1 Die exe als StandAlone (Dynam. RTL aus & Laufz.-Packages aus) erzeugt, 2 in einen beliebigen Ordner verschoben, 3 die Dll in ...\system32, 4 die exe gestartet.
Ergebnis lt TaskMgr:
Nach Start der exe: Speicherauslastg 3272k1x Btn 1 (ohne Dll): Speicherauslastg 3308k nach Zähler
Nx Btn 1 (ohne Dll): Speicherauslastg 3308k nach Zähler1.x Btn 2 (mit Dll): Speicherauslastg 4844k nach Zähler (kurz vor Ende des Zählers 5000k)
2.x Btn 2 (mit Dll): Speicherauslastg 5820k nach Zähler (kurz vor Ende des Zählers 7776k)
3.x Btn 2 (mit Dll): Speicherauslastg 6752k nach Zähler (kurz vor Ende des Zählers 8708k)
usw... Also nach jedem Durchlauf ca. 1000k mehr...

-
Entsprechend deines Quelltextauszuges lädst und entlädst du die dll bei jedem Aufruf von ImpWait. Ich meinte, dass du die dll nur einmal, beim Start deines Programmes lädst.
Wozu dient eigentlich das prozessmessages nach dem Sleep?
-
Braunstein schrieb:
Entsprechend deines Quelltextauszuges lädst und entlädst du die dll bei jedem Aufruf von ImpWait. Ich meinte, dass du die dll nur einmal, beim Start deines Programmes lädst.
Achso, die Dll gleich laden und die Dll-Fkt erst bei Bedarf - und dann ist das ganze noch dynamisch? Dann hab' ich wohl im Tutorial was falsch verstanden?

Braunstein schrieb:
Wozu dient eigentlich das prozessmessages nach dem Sleep?
Na wenn Du schon so fragst: hab' ich gar nicht drüber nachgedacht, als ich die Fkt aus meinem Prog in die Dll kopiert habe... Bei näherer Betrachtung ergibt das extrem wenig Sinn an dieser Stelle!
DankePS - Frage am Rande: Wo finde ich eigentlich Info's darüber, wo das Forum die letzten 2 Tage war? Hab noch nie davon gehört das ein Forum in Urlaub fährt...
-
Hallo
Kolumbus schrieb:
Braunstein schrieb:
Entsprechend deines Quelltextauszuges lädst und entlädst du die dll bei jedem Aufruf von ImpWait. Ich meinte, dass du die dll nur einmal, beim Start deines Programmes lädst.
Achso, die Dll gleich laden und die Dll-Fkt erst bei Bedarf - und dann ist das ganze noch dynamisch? Dann hab' ich wohl im Tutorial was falsch verstanden?

Ja das ist noch dynamisch. Statisch wäre komplett ohne LoadLibrary, sondern über eine lib-Datei
PS - Frage am Rande: Wo finde ich eigentlich Info's darüber, wo das Forum die letzten 2 Tage war? Hab noch nie davon gehört das ein Forum in Urlaub fährt...
Siehe hier.
bis bald
akari
-
Nur zum Verständnis - im Tutorial steht:
BCB-Tutorial schrieb:
Als nächsten Schritt wechseln wir zur Unit DLLImp.cpp. Hier erfolgt die Implementierung der ... Schnittstellen-Funktionen ... (diese Funktionen könnten natürlich auch einen beliebig anderen Namen erhalten):
int AddVals(int x, int y) { TAddVals* AddVals; //Variable des Zeigertyps. int Erg; HINSTANCE h = LoadLibrary("Project2.dll"); //DLL wird bei Bedarf geladen. //============================ if (h != 0) AddVals = (TAddVals*)GetProcAddress(h, "_AddVals"); if (AddVals != NULL) Erg = AddVals(x, y); //Aufruf der DLL-Funktion, hier also kein rekursiver Aufruf. else Erg = 0; FreeLibrary(h); //..und nach Gebrauch entladen. //============================= return Erg; }Warum steht das dann so im Tutorial, wenn es nicht richtig ist?

Meine Import.cpp (gleichwertig mit DllImp.cpp im Tut) nochmal:
// Project1.exe->Import.cpp #pragma hdrstop #include "Import.h" //--------------------------------------------------------------------------- #pragma package(smart_init) void ImpWait(int W) //RS| Definition der Schnittstellenfunktion { TWait* WaitAdr= NULL; // !!!!!!!!!!!!!!!! FEHLER??? !!!!!!!!!!!!!!! HINSTANCE HI= LoadLibrary("DynDll.dll"); if (HI != 0) { WaitAdr = (TWait*)GetProcAddress(HI, "_Wait"); } if (WaitAdr != NULL) WaitAdr(W); FreeLibrary(HI); }Kann es sein dass ein weiterer Fehler einfach da liegt, wo ich die vielen Ausrufezeichen gemacht habe? wenn ja, warum?
-
Ich habe nicht gesagt, dass das nicht richtig ist. Nur recht ineffektiv, wenn du die Funktion häufig brauchst.
Das, wo bei dir Fehler dran steht, ist keiner. Ich würde es nur ein wenig anders machen.void ImpWait(int W) //RS| Definition der Schnittstellenfunktion { HINSTANCE HI= LoadLibrary("DynDll.dll"); if( HI == 0) return; // Wenn die Bibliothek nicht geladen werden kann, kann man doch gleich rausgehen (evtl. Rückgabewert ?) TWait* WaitAdr = reinterpret_cast<TWait*>(GetProcAddress(HI, "_Wait")); //Ich benutze liber C++Casts statt C-Casts if( WaitAdr) WaitAdr(W); FreeLibrary(HI); }
-
Braunstein schrieb:
Nur recht ineffektiv, wenn du die Funktion häufig brauchst.
Ich kapier' das einfach nicht... Ich hab das Projekt zur Übung für spätere große Projekte gemacht - da stehen dann viele Fkt.en in einer Dll und die werden auch nicht ständig aufgerufen, sondern teilw. wahrscheinlich bloß 1-2Mal pro Anwendung (die Dll soll ja Fkt.en für mehrere Anwendungen zur Verfügung stellen). Und egal wie oft ich eine Dll-Fkt aufrufe - wenn sie dynamisch ist, muss sie doch auch wieder vollständig entladen werden!?! Da darf sich doch nix aufsummieren im Speicher!?! Erst recht nicht 1000k!?!

Und meine jetzige 2Button-Zähler-Anwendung ist doch nur ein kleines Testprog...
(Hand aufs Herz - wer bräuchte da ne dll mit ner Fkt sleep(int w)?)Braunstein schrieb:
Ich benutze liber C++Casts
Ich habe keine Ahnung was Casts sind!
Kannst Du das ein einem Satz darstellen?
-
z.Bsp.
Die Funktion GetProcAddress liefert als Ergebnis einen void-Zeiger (void*). Der kann auf so ziemlich alles zeigen, verwenden kannst du ihn aber so noch nicht. Du musst diesen void-Zeiger erst in einen Zeiger auf TWait umwandeln. Dieses Umwandeln nennt man auch einen Cast.
Du machst hier also nichts weiter, als deinem Compiler zu sagen
"Klar, das ist ein void-Zeiger. Ich weiß aber genau, das das eigentlich ein TWait-Zeiger ist, also behandle ihn auch so"
In deinem fall kann man auf zwei verschiedene Arten umwandeln
Die C-MethodeWaitAdr = (TWait*)GetProcAddress(HI, "_Wait");Die C++-Metode
TWait* WaitAdr = reinterpret_cast<TWait*>(GetProcAddress(HI, "_Wait"));Weiteres hierzu
http://tutorial.schornboeck.net/casts.htm
http://tutorial.schornboeck.net/reinterpret_cast.htm
http://tutorial.schornboeck.net/dynamic_cast.htm
-
So, ich habe jetzt mal Einiges versucht umzusetzen, was Ihr gepostet habt:
1. Ich benutze jetzt auch den C++ Cast. Die Tutorials hab' ich noch nicht gelesen, aber ich hoffe es findet sich dort bei späterem Lesen eine Erklärung, warum der C++ Cast besser ist (der C Cast ist nämlich eigentl. übersichtlicher, also leichter zu merken finde ich). Danke für die Erklärung zu casts!
2. Die dll wird jetzt nur beim Aufruf der Btn2-Klick-Behandlg. 1x geladen und 1x am Ende der Fkt entladen.
3. Das return; im Fehlerfall habe ich eingebaut, natürlich ist es sinnvoll dann abzubrechen!
Zusätzlich hab' ich vorm return ne Fehlermeldung über eine MessageBox eingebaut, falls die dll nich geladen werden kann. Warum MessageBox?throw std::logic_error("Cannot load *.dll");hat leider eine Fehlermeldung gebracht: [C++ Fehler] Anwendg.cpp(48): E2316 'logic_error' ist kein Element von 'std'
Ergebnisse (*prey* - es gibt Ergebnisse!):
* Die Speicherauslastung steigt nur noch beim 1. Starten der Zählschleife mit dll-delay um ca. 600k, dann immer so zw. 4 und 12k pro Durchlauf (reicht auch noch!)... bewege mich damit insgesamt im 4000k-Bereich, bei unbelasteter VCL-Anwendg.
* Die Anwendung funktioniert noch, inklusive beider Zählschleifen.
Fragen:
* Wie kann ich die Speicherauslastung noch weiter senken? Oder war's das?
(die 600k mehr nach dem 1. Starten der Dll-Fkt sind mir noch zuviel....)
* Ich hab einen Tip bekommen der hieß: "CodeGuard benutzen!" was sagt Ihr dazu?
* Ich hätte mir gerne die Speicherauslastung in einem Label anzeigen lassen, damit ich nicht immer den TaskMgr öffnen muss - gebt Ihr mir dazu einen Hinweis? (Ich mach' aber auch gern nen neuen Thread auf...
)MfG
Ein sehr dankbarer Entdecker
-
zu 1.
Vielleicht leichter zu merken, aber viel schwerer im Code zu finden. Zudem wandelt ein C-Cast immer um, egal ob das wirklich geht oder nicht. In deinem Fall ist das zwar egal, in den meisten andreren Fällen aber nicht.zu 3.
Bau mal ein#include <stdexcept>mit ein. Dann sollte es gehen.
Um die 600k wirst du wohl nicht rumkommen.
CodeGuard ist immer eine gute Idee. Solange die Projekte noch klein sind kannst du damit leicht Fehler finden.
Standardmäßig gibt es (glaube ich) nichts um die Speicherauslastung anzuzeigen. Es gibt aber genug externe Komponenten, die das können. Einfach mal suchen. Ich würde in einem solchen Fall wohl die LMD-Tools nehmen.
-
Kolumbus schrieb:
1. Ich benutze jetzt auch den C++ Cast. Die Tutorials hab' ich noch nicht gelesen, aber ich hoffe es findet sich dort bei späterem Lesen eine Erklärung, warum der C++ Cast besser ist (der C Cast ist nämlich eigentl. übersichtlicher, also leichter zu merken finde ich).
http://c-plusplus.net/forum/viewtopic-var-p-is-1254559.html#1254559
-
Ok, Danke ihr Beiden - so langsam fügt sich ein Bild, wenn auch noch sehr verschwommen - ich erkenne schon langsam die Familie cast...
Braunstein schrieb:
#include <stdexcept>dann war ich ja mit dem Versuch
#include <stdio>schon auf dem richtigen Weg... (auch wenn's noch n paar Meter entfernt ist).
Aber ich werd's mal testen, Danke!Edit: Der Dll-Ladefehler gefällt mir mit ner MessageBox besser - da kenn ich mich mit aus und da steht nicht nur "Externe Exception EEFFACE" drin... Warum sollte ich noch extra ne Datei includieren, wenn's auch so geht? Oder habe ich davon Vorteile?
Braunstein schrieb:
CodeGuard ist immer eine gute Idee. Solange die Projekte noch klein sind kannst du damit leicht Fehler finden.
Auch das werde ich dann bei Gelegenheit mal in Angriff nehmen. Viell. hilfts mir ja dann doch bei mittelgroßen Projekten auch?!
Zum Thema Speicherauslastung: Hab da im msdn was von GetCurrentProcess / GetProcessWorkingSet / GetProcess... gefunden, ist davon nich was zum Speicherauslastung anzeigen. GetProcessWorkingSetSize ist ja irgendwie für Minimum und Maximum des physikalischen Speichers, nicht für die aktuelle Gesamt-Auslastung!?!

Wenn es innerhalb der üblichen Anweisungen möglich wäre, wär' mir schon lieber als externe Komponenten...
-
So, hab jetzt mal folgendes in mein Prog eingebaut:
HANDLE hCurProc= GetCurrentProcess(); DWord ProcID= GetCurrentProcessId(); Label1->Caption= ProcID; //Anzeige der Prozess-ID bool WSISucceed; int kMaker= 1024; unsigned long MinMem=0; unsigned long MaxMem=0; unsigned long *pMinMem= &MinMem; unsigned long *pMaxMem= &MaxMem; WSISucceed= GetProcessWorkingSetSize(hCurProc, pMinMem, pMaxMem); if (WSISucceed== 1) { Label2->Caption= IntToStr(*pMinMem / kMaker) + "k"; //Anzeige der WorkingSetSize Label3->Caption= IntToStr(*pMaxMem / kMaker) + "k"; } PROCESS_MEMORY_COUNTERS P_M_C; GetProcessMemoryInfo(hCurProc, &P_M_C, sizeof(P_M_C)); Label4->Caption= P_M_C.PagefileUsage; //Anzeige der Speicherauslastung CloseHandle(hCurProc);nur leider bekomme ich folgende Fehlermeldung:
[Linker Fehler] Error: Ungelöste externe 'GetProcessMemoryInfo' referenziert von C:\...\DLL-PROJEKT\DEBUG_BUILD\ANWENDG.OBJ
seit ich diesen Code dazugeschrieben habe:
PROCESS_MEMORY_COUNTERS P_M_C; GetProcessMemoryInfo(hCurProc, &P_M_C, sizeof(P_M_C)); Label4->Caption= P_M_C.PagefileUsage; //Anzeige der Speicherauslastung#include "PSAPI.h"hab' ich auch drin.
Jetzt hatte ich schon fast Hoffnung, dass mein Prog seine Speicherauslastung anzeigt...
Oder ist PagefileUsage wieder nicht die Speicherauslastung? Wo könnte das Problem liegen?
-
Binde mal die Psapi.lib mit in dein Projekt ein. Die dürfte unter deinem Builderverzeichnis\Lib\Psdk liegen.
-
Das genannte Verzeichnis ist vorhanden, die psapi.lib ist drin und das Verzeichnis ist als Bibliothekssuchpfad in den Linkeroptionen angegeben... Was fehlt?
-
Hallo
Du must die genannte lib noch über Projekt/Dem Projekt hinzufügen einbinden (Dateityp im Dialog auf lib stellen)
bis bald
akari