Delphi-PRAXiS
Seite 1 von 2  1 2      

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Sonstige Fragen zu Delphi (https://www.delphipraxis.net/19-sonstige-fragen-zu-delphi/)
-   -   Delphi Speicherlast des Programms mittels GetMemoryManagerState - komischer Wert (https://www.delphipraxis.net/154621-speicherlast-des-programms-mittels-getmemorymanagerstate-komischer-wert.html)

moelski 18. Sep 2010 06:40

Speicherlast des Programms mittels GetMemoryManagerState - komischer Wert
 
Moin !

Wenn ich hiermit versuche die Speicherlast meiner Anwendung auszugeben ...
Delphi-Quellcode:
procedure TForm1.Timer1Timer(Sender: TObject);
var State: TMemoryManagerState;
    Size, i : Cardinal;
begin
  GetMemoryManagerState(State);
    Size := 0;
    for i := 0 to High(State.SmallBlockTypeStates) do
      Inc(Size, State.SmallBlockTypeStates[i].AllocatedBlockCount *
              State.SmallBlockTypeStates[i].UseableBlockSize);
    Inc(Size, State.TotalAllocatedMediumBlockSize +
              State.TotalAllocatedLargeBlockSize);

  Form1.Caption := 'Item Assign Test - ' +
                   IntToStr(Size) + ' Memusage';
end;
Dann kriege ich als Wert ~100780.

Nur was sagt mir dieser Wert? 100kb Speicher den meine Anwendung nutzt?!
Der Taskmanager sagt nämlich was von 3,8MB ...

Sir Rufo 18. Sep 2010 08:56

AW: Speicherlast des Programms mittels GetMemoryManagerState - komischer Wert
 
Versuch es mal so:
Delphi-Quellcode:
function GetMemoryReserved : Cardinal;
  var
    State : TMemoryManagerState;
    idx : Integer;
  begin
    Result := 0;
    GetMemoryManagerState( State );
    for idx := low( State.SmallBlockTypeStates ) to high( State.SmallBlockTypeStates ) do
      Result := Result + State.SmallBlockTypeStates[ idx ].ReservedAddressSpace;
    Result := Result + State.ReservedMediumBlockAddressSpace + State.ReservedLargeBlockAddressSpace;
  end;
Dem System ist es schnurzpiepe, was du an Speicher tatsächlich belegst.
Es zählt was du reservierst (auf welcher Liege liegt dein Handtuch), denn das ist für das System auch nicht mehr nutzbar und gilt somit als belegt/benutzt.

Benmik 7. Nov 2016 18:16

AW: Speicherlast des Programms mittels GetMemoryManagerState - komischer Wert
 
Wenn ich diese Leiche nochmal exhumieren darf: Wenn ich mit diesem Code messe, erhalte ich einen Wert von 5.000.000, während der Taskmanager mir 500.000.000 anzeigt. Was läuft da schief?

Hintergrund ist, dass ich eine variable Anzahl von Bitmaps (aus JPG) in den Speicher vorlade. Sind die Bitmaps sehr groß, gibt es eine EOutOfMemory. Ich würde das gern vorher wissen und vermeiden. Laut Taskmanager-Anzeigen ist bei ca. 1,8 GB Schluss. Wie messe ich das jetzt, was wäre ein Schwellenwert, der nicht überschritten werden sollte (den theoretischen Wert von 2 GB bei einer 32 Bit-Anwendung kenne ich natürlich)?

Jim Carrey 7. Nov 2016 18:46

AW: Speicherlast des Programms mittels GetMemoryManagerState - komischer Wert
 
Nur zum Test!
Setze mal folgendes in deine DPR-Datei relativ an den Anfang und mache deinen Test mit den Bildern noch einmal:
Delphi-Quellcode:
//

{$SETPEFLAGS IMAGE_FILE_LARGE_ADDRESS_AWARE}

//
Damit solltest von von 2 GB auf etwa 3 GB maximalen Verbrauch kommen können.

Benmik 7. Nov 2016 19:00

AW: Speicherlast des Programms mittels GetMemoryManagerState - komischer Wert
 
Das führt leider nicht weiter. Selbst wenn es funktionierte, käme die Speichererschöpfung halt nur etwas später. Es erklärt auch nicht, warum Sir Rufos Code nicht zu funktionieren scheint.

Jim Carrey 7. Nov 2016 20:33

AW: Speicherlast des Programms mittels GetMemoryManagerState - komischer Wert
 
Guck mal hier:

Delphi-Quellcode:
procedure TForm1.Button1Click(Sender: TObject);
 function GetCurrentProcessMemorySize: Int64;
 var
  nWndHandle, nProcID, nTmpHandle: HWND;
  pPMC: PPROCESS_MEMORY_COUNTERS;
  pPMCSize: Cardinal;
 begin
  nWndHandle := Application.Handle;
  if nWndHandle = 0 then
   begin
    Result := 0;
    Exit;
   end;
  pPMCSize := SizeOf(PROCESS_MEMORY_COUNTERS);
  GetMem(pPMC, pPMCSize);
  pPMC^.cb := pPMCSize;
  GetWindowThreadProcessId(nWndHandle, @nProcID);
  nTmpHandle := OpenProcess(PROCESS_ALL_ACCESS, False, nProcID);
  if (GetProcessMemoryInfo(nTmpHandle, pPMC, pPMCSize)) then
   Result := pPMC^.WorkingSetSize
  else
   Result := 0;
  FreeMem(pPMC);
  CloseHandle(nTmpHandle);
 end;

begin
 ShowMessage(IntToStr(GetCurrentProcessMemorySize)));
end;
Das zeigt den Arbeitssatz an.

Quelle: http://snipplr.com/view/38214/get-process-memory-usage/

Benmik 7. Nov 2016 20:39

AW: Speicherlast des Programms mittels GetMemoryManagerState - komischer Wert
 
Wirklich vielen Dank für deine Mühe, Jim, aber das zeigt - wenn ich mich nicht irre - nur den Speicherverbrauch des laufenden Prozesses an. EOutOfMemory kommt aber bei der Gesamtgrenze für das ganze Programm.

Jim Carrey 7. Nov 2016 20:43

AW: Speicherlast des Programms mittels GetMemoryManagerState - komischer Wert
 
Ich dachte du suchst eine Funktion die den Speicherverbrauch anzeigt? :pale:

Das mit dem OutOfMemory... wieso fängst du den nicht ab und wenn er auftritt, brichst du die aktuelle Funktion ab?

Delphi-Quellcode:
for i := 0 to 100 do
 begin
  try
   tueirgendetwas;
  except
   on E: EOutOfMemory // <===========
    begin // <===========
     Break; // <===========
    end; // <===========
  end;
 end;
Ich weiß nicht, ob man das so macht, aber was anderes fällt mir nicht ein.

Zacherl 7. Nov 2016 21:21

AW: Speicherlast des Programms mittels GetMemoryManagerState - komischer Wert
 
Zitat:

Zitat von Jim Carrey (Beitrag 1352927)
Guck mal hier:

Wer auch immer diesen Code verbrochen hat, hat absolut keine Ahnung von der Win32 API. Den ganzen Quatsch mit MSDN-Library durchsuchenGetWindowThreadProcessId und MSDN-Library durchsuchenOpenProcess kann man sich sparen und stattdessen einfach MSDN-Library durchsuchenGetCurrentProcess bzw.
Delphi-Quellcode:
THandle(-1)
an MSDN-Library durchsuchenGetProcessMemoryInfo übergeben.

Zitat:

Zitat von Benmik (Beitrag 1352928)
Wirklich vielen Dank für deine Mühe, Jim, aber das zeigt - wenn ich mich nicht irre - nur den Speicherverbrauch des laufenden Prozesses an. EOutOfMemory kommt aber bei der Gesamtgrenze für das ganze Programm.

Diese Aussage verstehe ich nicht ganz. Eine feste Obergrenze gibt es nicht. Mit der 2 GiB Grenze für 32-Bit Prozesse hast du zwar recht, allerdings reservierst du (oder die Delphi RTL, oder Funktionen, die du verwendest) ja mehr oder weniger unvorhersehbar noch Speicher in Form von Objekten, Strings, Arrays, etc.
Dann kommt noch hinzu, dass große zusammenhängende Speicherbereiche schwieriger zu finden sind, als kleine Blöcke. Je nach Fragmentierung des Heaps kann es dann mal funktionieren und ein anderes Mal nicht.

Zitat:

Zitat von Jim Carrey (Beitrag 1352929)
Das mit dem OutOfMemory... wieso fängst du den nicht ab und wenn er auftritt, brichst du die aktuelle Funktion ab?

Delphi-Quellcode:
EOutOfMemory
abfangen ist okay, aber meiner Meinung nach nicht optimal, da du dies dann bei so wirklich jedem
Delphi-Quellcode:
Create
,
Delphi-Quellcode:
GetMem
,
Delphi-Quellcode:
SetLength
, etc. machen müsstest. Natürlich auch bei allen Funktionen, die intern irgendwie Speicher auf dem Heap reservieren.

Du solltest mal schauen, ob es wirklich notwendig ist ALLE Bitmaps permanent im Speicher zu halten.

Jim Carrey 7. Nov 2016 21:26

AW: Speicherlast des Programms mittels GetMemoryManagerState - komischer Wert
 
Zitat:

Du solltest mal schauen, ob es wirklich notwendig ist ALLE Bitmaps permanent im Speicher zu halten.
Finde ich auch ein bisschen komisch... wie viele Instanzen sind das denn, dass du an die 2 GB bzw 3 GB Grenze kommst?

Bei mir persönlich waren Speicherprobleme ausnahmslos immer auf schlechten Code zurückzuführen.

Benmik 7. Nov 2016 21:57

AW: Speicherlast des Programms mittels GetMemoryManagerState - komischer Wert
 
Nein, nein, das hat schon seine Richtigkeit. Es geht um ein Bildanzeigeprogramm. Da biete ich eine Möglichkeit, eine einstellbare Anzahl von Bildern im Speicher vorzuladen, zum Beispiel 5 vor und 2 zurück. Man kann dann auch bei langsamer Verbindung sehr schnell innerhalb dieser 7 Bilder hin- und herspringen, weil sie nicht jedesmal geladen und dekodiert werden müssen. 7 Bitmaps sind schon eine Menge Stoff, das kann locker an 500 MB herangehen. Ein EOutOfMemory gibt es, wenn a) die Anzahl zu hoch eingestellt wird, b) die Bitmaps sehr groß sind. Wenn da ein Panoramabild dabei ist, dann kann es schon bei einer Bitmap knallen.
Das Abfangen von EOutOfMemory funktioniert leider nicht immer, ich vermute, weil da Funktionen aus einer DLL aufgerufen werden, und deren Fehlermeldungen nicht unterdrückt werden können. Hab ich aber noch nicht ganz exakt untersucht, weil ich es lieber gesehen hätte, zum Beispiel bei 1,5 GB das Laden abbrechen zu können bzw. gar nicht erst zu versuchen. Das sollte doch eigentlich mit GetMemoryManagerState möglich sein. Daneben verstehe ich auch nicht: Wie kann die Berechnung um den Faktor 100 daneben liegen?

Jim Carrey 7. Nov 2016 21:59

AW: Speicherlast des Programms mittels GetMemoryManagerState - komischer Wert
 
Zitat:

Da biete ich eine Möglichkeit, eine einstellbare Anzahl von Bildern im Speicher vorzuladen, zum Beispiel 5 vor und 2 zurück.
Ach so ok!

Da fällt mir spontan ein, wie es Photoshop doch macht. Da werden die Bilder (Tabs) auf die Festplatte ausgelagert).
Wäre das eine Möglichkeit für dich von der Schnelligkeit her?

Benedikt Magnus 7. Nov 2016 22:12

AW: Speicherlast des Programms mittels GetMemoryManagerState - komischer Wert
 
Oder, wenn das Auslagern auf die Festplatte wirklich zu langsam sein sollte, einfach die Größe der Bitmaps ermitteln und nach oben hin deckeln?

Benmik 7. Nov 2016 22:15

AW: Speicherlast des Programms mittels GetMemoryManagerState - komischer Wert
 
Auf keinen Fall. Die Bilder können ja auch auf einem unglaublich langsamen Netzlaufwerk liegen (ich spreche da aus eigener Erfahrung). Außerdem funktioniert das mit dem Speicher ganz ausgezeichnet. Es handelt sich ja nur um ein Grenz-Problem.

Deckeln: Ja, aber das hängt ja vom Speicher ab... Obwohl, könnte ich als Notnagel natürlich auch machen... Aber Gottsakra, warum funktioniert denn die Speichermessung nicht?!

Jim Carrey 7. Nov 2016 22:22

AW: Speicherlast des Programms mittels GetMemoryManagerState - komischer Wert
 
Zitat:

Aber Gottsakra, warum funktioniert denn die Speichermessung nicht?!
Funktioniert mein kopierter Code nicht?

Zacherl 7. Nov 2016 22:44

AW: Speicherlast des Programms mittels GetMemoryManagerState - komischer Wert
 
Wie wäre es denn hiermit:
Delphi-Quellcode:
type
  TMemoryFileStream = class(THandleStream)
  public
    constructor Create;
    destructor Destroy; override;
  end;

{ TMemoryFileStream }

constructor TMemoryFileStream .Create;
begin
  inherited Create(CreateFile(PChar(TPath.GetTempFileName), GENERIC_READ or GENERIC_WRITE,
    0, nil, CREATE_ALWAYS, FILE_ATTRIBUTE_TEMPORARY or
    FILE_ATTRIBUTE_NOT_CONTENT_INDEXED or FILE_ATTRIBUTE_HIDDEN or FILE_FLAG_DELETE_ON_CLOSE, 0));
end;

destructor TMemoryFileStream .Destroy;
begin
  if (FHandle <> INVALID_HANDLE_VALUE) then
  begin
    FileClose(FHandle);
  end;
  inherited Destroy;
end;
Du zeigst ja immer nur ein Bild gleichzeitig an, so wie ich das verstanden habe. Dieses Bild lädst du von seinem originalen Speicherort, decodierst es ggfls., so dass du die Bilddaten als
Delphi-Quellcode:
TBitmap
vorliegen hast und zeigst es dann an. Sobald der User jetzt das nächste Bild anzeigen will, speicherst du das aktuelle Bild mit der von mir geposteten Stream-Klasse, gibst das
Delphi-Quellcode:
TBitmap
frei und wiederholst die Aktionen zum Anzeigen des neuen Bildes. Soll nun eines der vorherigen Bilder angezeigt werden, lädst du das
Delphi-Quellcode:
TBitmap
einfach wieder aus dem Stream. Diese Stream-Klasse ist ziemlich performant, da sie den Windows File-Cache verwendet. Es sieht zwar so aus, als ob die Daten in einer temporären Datei gespeichert werden, aber so lange Windows global genug freien RAM zur Verfügung stehen hat, bleiben die Daten dort im Cache. Zugriffe auf die Festplatte hast du so also im Optimalfall gar nicht und als Bonus managed Windows den kompletten Speicher für dich.
Delphi-Quellcode:
EOutOfMemory
Exceptions wären damit auf jeden Fall Geschichte.

Benmik 7. Nov 2016 22:53

AW: Speicherlast des Programms mittels GetMemoryManagerState - komischer Wert
 
Zitat:

Zitat von Jim Carrey (Beitrag 1352939)
Funktioniert mein kopierter Code nicht?

Den Code kannte ich schon von diversen stackoverflow-Diskussionen, aber misst der nicht nur den aktuellen Prozess und nicht den Gesamtspeicher, den die komplette Anwendung verbraucht?

Zitat:

Zitat von Zacherl (Beitrag 1352940)
Wie wäre es denn hiermit:

Das ist ja interessant! Das werde ich mal ausprobieren.

t.roller 7. Nov 2016 23:03

AW: Speicherlast des Programms mittels GetMemoryManagerState - komischer Wert
 
Very Large Bitmap Experiment
http://www.efg2.com/Lab/Graphics/VeryLargeBitmap.ZIP

Benmik 7. Nov 2016 23:18

AW: Speicherlast des Programms mittels GetMemoryManagerState - komischer Wert
 
Süß! Windows 95! Delphi 3! Toll!
Aber auch die maximale Größe, die das Programm annimmt, zwingt mein System nicht in die Knie.
Projekt öffnen und modifizieren habe ich mir allerdings gespart.

Bambini 8. Nov 2016 08:05

AW: Speicherlast des Programms mittels GetMemoryManagerState - komischer Wert
 
Zitat:

Zitat von Benmik (Beitrag 1352942)
Zitat:

Zitat von Jim Carrey (Beitrag 1352939)
Funktioniert mein kopierter Code nicht?

Den Code kannte ich schon von diversen stackoverflow-Diskussionen, aber misst der nicht nur den aktuellen Prozess und nicht den Gesamtspeicher, den die komplette Anwendung verbraucht?

Dann fragt doch beim FastMM4 Memory Manager nach, wie viel deine Anwendung gerade verwendet:
Delphi-Quellcode:
var
  LMem : TMemoryManagerUsageSummary;
begin
  GetMemoryManagerUsageSummary(LMem);
  Caption := LMem.AllocatedBytes.ToString + ' Bytes';

t.roller 8. Nov 2016 14:53

AW: Speicherlast des Programms mittels GetMemoryManagerState - komischer Wert
 
How Much Memory Is Your Delphi Program Occupying?

Zacherl 8. Nov 2016 15:01

AW: Speicherlast des Programms mittels GetMemoryManagerState - komischer Wert
 
Zitat:

Zitat von t.roller (Beitrag 1353000)

Der Artikel ist nebenbei erwähnt inkorrekt. Memory Leaks kann man nicht zuverlässig über die WorkingSetSize detecten. Ich verweise auf folgenden Beitrag: http://stackoverflow.com/a/1986486. Die WorkingSetSize umfasst nämlich nur den Speicher des Prozesses, der momentan tatsächlich im physikalischen RAM in Verwendung ist. Grade bei Memory Leaks wird man auf die "vergessenen" Speicherstellen nie wieder zugreifen, weshalb Windows die betroffenen Pages früher oder später auslagert. Die WorkingSetSize verringert sich danach wieder um die ausgelagerte Datenmenge, das Memory Leak ist aber trotzdem vorhanden und verschwendet Platz im virtuellen Adressbereich.

Benmik 8. Nov 2016 17:25

AW: Speicherlast des Programms mittels GetMemoryManagerState - komischer Wert
 
Zitat:

Zitat von Zacherl (Beitrag 1353001)
Der Artikel ist nebenbei erwähnt inkorrekt.

Inkorrekt! INkorrekt !!! Wann habe ich das zum letzten Mal gelesen? Danach folgt aber "detecten"... Man weiß nicht mehr, was man denken soll.

Ich habe erstmal Benedikt Magnus' Anregung aufgegriffen und die Größe der 7 Bitmaps ermittelt:
Delphi-Quellcode:
SizeOf(BMP) + Bmp.Width * Bmp.Height * (24 div 8)
. Das ergab ungefähr 370 MB. Dann habe ich Bambinis Anregung umgesetzt, das ergab einen Wert von ca. 2,7 MB. Der Taskmanager zeigt ca. 500 MB. Das verstehe, wer will.

Die Umsetzung von Zacherls Hinweis wird etwas aufwändiger und dauern.

t.roller 8. Nov 2016 18:30

AW: Speicherlast des Programms mittels GetMemoryManagerState - komischer Wert
 
Zitat:

Zitat von Zacherl (Beitrag 1353001)
...Der Artikel ist nebenbei erwähnt inkorrekt. Memory Leaks kann man nicht zuverlässig über die WorkingSetSize detecten.

Was soll ich mit dieser Aussage anfangen?
Beispiel:
Wenn ich ein Auto habe mit im Fahrzeugschein eingetragener Länge von 4,00 Meter:
Ist das Auto 4,00 Meter lang oder ist es kürzer, weil ein LKW-Fahrer hinten draufgefahren ist?

Memory-Leaks sind nicht der Normalfall. Redet das niemanden ein!
MSDN verwendet auch die WorkingSetSize - was sonst.

In PSAPI.PAS:
Delphi-Quellcode:
  _PROCESS_MEMORY_COUNTERS = record
    cb: DWORD;
    PageFaultCount: DWORD;
    PeakWorkingSetSize: SIZE_T;
    WorkingSetSize: SIZE_T;
    QuotaPeakPagedPoolUsage: SIZE_T;
    QuotaPagedPoolUsage: SIZE_T;
    QuotaPeakNonPagedPoolUsage: SIZE_T;
    QuotaNonPagedPoolUsage: SIZE_T;
    PagefileUsage: SIZE_T;
    PeakPagefileUsage: SIZE_T;
  end;

Benedikt Magnus 8. Nov 2016 18:40

AW: Speicherlast des Programms mittels GetMemoryManagerState - komischer Wert
 
Zitat:

Zitat von Benmik (Beitrag 1353027)
Ich habe erstmal Benedikt Magnus' Anregung aufgegriffen und die Größe der 7 Bitmaps ermittelt:
Delphi-Quellcode:
SizeOf(BMP) + Bmp.Width * Bmp.Height * (24 div 8)
. Das ergab ungefähr 370 MB. Dann habe ich Bambinis Anregung umgesetzt, das ergab einen Wert von ca. 2,7 MB. Der Taskmanager zeigt ca. 500 MB. Das verstehe, wer will.

Bedenke, dass das Programm selbst ja auch noch Speicher braucht (plus evtl. Overhead). Außerdem wird Speicher, den das Programm einmal belegt hat, nicht unbedingt direkt wieder freigegeben.
Wenn du also eine Pauschale für das Programm selbst nimmst und dir merkst, wie viele MB an Bitmaps bisher maximal gleichzeitig im Speicher lagen, müsstest du etwas erhalten, dass garantiert unter dem Wert im Taskmanager liegt (und bei voller oder konstanter Auslastung an Bitmaps sogar nahe am Taskmanager).

Jim Carrey 8. Nov 2016 18:50

AW: Speicherlast des Programms mittels GetMemoryManagerState - komischer Wert
 
Zitat:

Zitat von Benedikt Magnus (Beitrag 1353037)
Außerdem wird Speicher, den das Programm einmal belegt hat, nicht unbedingt direkt wieder freigegeben.

Design Your Delphi Program in a Way That it Keeps its Memory Usage in Check

Zacherl 8. Nov 2016 19:05

AW: Speicherlast des Programms mittels GetMemoryManagerState - komischer Wert
 
Zitat:

Zitat von t.roller (Beitrag 1353034)
Zitat:

Zitat von Zacherl (Beitrag 1353001)
...Der Artikel ist nebenbei erwähnt inkorrekt. Memory Leaks kann man nicht zuverlässig über die WorkingSetSize detecten.

Was soll ich mit dieser Aussage anfangen?
[...]
Memory-Leaks sind nicht der Normalfall. Redet das niemanden ein!

Wo habe ich das denn behauptet? Ganz im Gegenteil sogar. Ich bezog mich ledigliech auf den von dir verlinkten Artikel, welcher mit diesem Satz beginnt:
Zitat:

No matter how much you try to code without bugs and memory leaks .. the more complex the application the more possible is to leak some memory.
Folglich impliziere ich, dass der danach gezeigte Code (welcher WorkingSetSize verwendet) dazu gedacht ist, Memory Leaks aufzuspüren. Das ist damit aber NICHT zuverlässig möglich (siehe den von mir verlinkten Stackoverflow Post). Stattdessen müsste PrivateBytes bzw. PagefileUsage verwendet werden.

Zitat:

Zitat von t.roller (Beitrag 1353034)

Die Relevanz dieses Beispiels sehe ich ebenfalls nicht. Hier wird einfach nur das komplette Struct gedumpt.

BUG 8. Nov 2016 19:20

AW: Speicherlast des Programms mittels GetMemoryManagerState - komischer Wert
 
Zitat:

Zitat von Jim Carrey (Beitrag 1353038)
Zitat:

Zitat von Benedikt Magnus (Beitrag 1353037)
Außerdem wird Speicher, den das Programm einmal belegt hat, nicht unbedingt direkt wieder freigegeben.

Design Your Delphi Program in a Way That it Keeps its Memory Usage in Check

Freigeben im Sinne von new/delete gibt Speicher wieder an den Memory-Manager zurück. Im Regelfall gibt der den Speicher aber nicht wieder an das Betriebssystem zurück, sondern behält ihn um zukünftige Anfragen zu erfüllen. Dass hängt natürlich von der Implementierung des MM ab, und häufig von der Größe der Anfragen. Wenn Seiten aus solchem Speicher eine Weile nicht benutzt werden, fallen sie eventuell aus dem Workingset und werden ausgepaged.

Zitat:

Zitat von Zacherl (Beitrag 1353043)
Folglich impliziere ich, dass der danach gezeigte Code (welcher WorkingSetSize verwendet) dazu gedacht ist, Memory Leaks aufzuspüren. Das ist damit aber NICHT zuverlässig möglich (siehe den von mir verlinkten Stackoverflow Post). Stattdessen müsste PrivateBytes bzw. PagefileUsage verwendet werden.

Korrekt. Die Größe des Workingsets ist eine Entscheidung des Betriebssystems.

Zacherl 8. Nov 2016 19:22

AW: Speicherlast des Programms mittels GetMemoryManagerState - komischer Wert
 
Der Punkt mit den Forms ist gut, aber mit MSDN-Library durchsuchenSetProcessWorkingSetSize die Pages zwangsweise auslagern zu wollen ist in meinen Augen großer Schwachsinn. Windows macht das nämlich ganz von alleine, wenn die betroffene Page einige Zeit lang nicht mehr verwendet wurde. Greift das Programm irgendwann wieder darauf zu, gibt es ein Page Fault und der Windows Memory Manager läd die Seite wieder in den RAM. Da sind im Hintergrund Algorithmen aktiv, die ziemlich gute Vorhersagen treffen.

Außerdem adressiert die diese API wieder nicht das eigentliche Problem. Also nochmal: Die großen Bitmaps erfordern große zusammenhängende Speicherblöcke im virtuellen Adressraum. Die Pages auszulagern entlastet aber nicht den virtuellen Adressraum, sondern nur den physikalischen Speicher.

Deshalb kann ich auch nur nochmal empfehlen die ganze Herangehensweise mit Messen des aktuell benutzten Speichers so schnell wie möglich zu vergessen. Man kann einfach nicht zuverlässig vorhersagen ab welcher Größe ein
Delphi-Quellcode:
EOutOfMemory
auftreten wird. Wie gesagt kann es sogar passieren, dass dein Prozess noch kaum Arbeitsspeicher verwendet, aber dank Fragmentierung kein genügend großer zusammenhängender Block verfügbar ist. Kannst du ganz leicht testen, indem du mal 1 GiB Speicher am Stück mit
Delphi-Quellcode:
GetMem
anforderst. Dieser Aufruf wird bei einem 32-Bit Programm in den meisten Fällen direkt scheitern. 1 GiB in mehreren kleinen 10 MiB Blöcken anzufordern, wird dahingegen sehr wahrscheinlich Erfolg haben. Auch wirst du merken, dass dein Bitmap Tool als 64-Bit Kompilat ohne Änderung vermutlich sehr viele Bitmaps mehr laden kann. Schlussfolgerung ist, dass nicht der physikalische RAM das Limit darstellt, sondern der virtuelle Adressraum.

Jim Carrey 8. Nov 2016 20:21

AW: Speicherlast des Programms mittels GetMemoryManagerState - komischer Wert
 
Das mit den Forms mache ich teilweise auch so. Ich erstelle komplexe Formulare erst wenn sie wirklich benötigt werden.
Das verschnellert den Programmstart und hält den Speicherverbrauch ein wenig niedriger.
Hat einzig den Nachteil der Usability im Sinne von "Wenn ich da drauf klicke, soll sofort was passieren" - ist dann eben nicht der Fall.

Bambini 9. Nov 2016 09:15

AW: Speicherlast des Programms mittels GetMemoryManagerState - komischer Wert
 
Zitat:

Zitat von Jim Carrey (Beitrag 1353053)
Das mit den Forms mache ich teilweise auch so. Ich erstelle komplexe Formulare erst wenn sie wirklich benötigt werden.
Das verschnellert den Programmstart und hält den Speicherverbrauch ein wenig niedriger.
Hat einzig den Nachteil der Usability im Sinne von "Wenn ich da drauf klicke, soll sofort was passieren" - ist dann eben nicht der Fall.

... das wäre doch eine schöne Aufgabe für einen Hintergrund-Thread.

Benmik 9. Nov 2016 14:32

AW: Speicherlast des Programms mittels GetMemoryManagerState - komischer Wert
 
Jetzt habe ich Zacherls Vorschlag implementiert. Funktioniert auch. Aufgrund der Streams kann die Größe der Bitmaps jetzt genau angegeben werden. Der Taskmanager - der ja augenscheinlich erschütternd unzuverlässig ist - zeigt nun statt 500 MB nur noch 180 MB an.
Die ganze Geschichte läuft aber merklich langsamer ab als mit den direkt gespeicherten Bitmaps. Die Erstellung der Bitmaps/Streams ist in asychrone Threads ausgelagert, auch damit scheint es Probleme zu geben, das Programm hängt sich bei forcierter Belastung auf ("forcierte Belastung" heißt, ich halte die Weiter-Taste gedrückt und nudle 100 Bilder durch, dadurch müssen pausenlos Bilder geladen, gelesen und verworfen werden). Hab's jetzt aber noch nicht richtig handoptimiert, mal sehen.

Bambini 9. Nov 2016 15:13

AW: Speicherlast des Programms mittels GetMemoryManagerState - komischer Wert
 
Zitat:

Zitat von Benmik (Beitrag 1353138)
Jetzt habe ich Zacherls Vorschlag implementiert. Funktioniert auch. Aufgrund der Streams kann die Größe der Bitmaps jetzt genau angegeben werden. Der Taskmanager - der ja augenscheinlich erschütternd unzuverlässig ist - zeigt nun statt 500 MB nur noch 180 MB an.
Die ganze Geschichte läuft aber merklich langsamer ab als mit den direkt gespeicherten Bitmaps.

Nun ja, der Stream liegt damit jetzt nicht mehr im RAM sondern alle auf der Platte. Auch wenn Windows hierbei einiges puffert. So schnell wie RAM ist eine Platte nie ;-)

Benmik 9. Nov 2016 16:00

AW: Speicherlast des Programms mittels GetMemoryManagerState - komischer Wert
 
Laut Zacherl müsste das alles im RAM liegen.

Bambini 9. Nov 2016 16:43

AW: Speicherlast des Programms mittels GetMemoryManagerState - komischer Wert
 
Zitat:

Zitat von Benmik (Beitrag 1353160)
Laut Zacherl müsste das alles im RAM liegen.

wenn du diesen Code verwendest:
Delphi-Quellcode:
 inherited Create(CreateFile(PChar(TPath.GetTempFileName), GENERIC_READ or GENERIC_WRITE,
    0, nil, CREATE_ALWAYS, FILE_ATTRIBUTE_TEMPORARY or
    FILE_ATTRIBUTE_NOT_CONTENT_INDEXED or FILE_ATTRIBUTE_HIDDEN or FILE_FLAG_DELETE_ON_CLOSE, 0));
dann nicht.

Zacherl 9. Nov 2016 17:13

AW: Speicherlast des Programms mittels GetMemoryManagerState - komischer Wert
 
Zitat:

Zitat von Benmik (Beitrag 1353160)
Laut Zacherl müsste das alles im RAM liegen.

Zitat:

Zitat von Bambini (Beitrag 1353165)
Zitat:

Zitat von Benmik (Beitrag 1353160)
Laut Zacherl müsste das alles im RAM liegen.

wenn du diesen Code verwendest:
Delphi-Quellcode:
 inherited Create(CreateFile(PChar(TPath.GetTempFileName), GENERIC_READ or GENERIC_WRITE,
    0, nil, CREATE_ALWAYS, FILE_ATTRIBUTE_TEMPORARY or
    FILE_ATTRIBUTE_NOT_CONTENT_INDEXED or FILE_ATTRIBUTE_HIDDEN or FILE_FLAG_DELETE_ON_CLOSE, 0));
dann nicht.

Beachte das
Delphi-Quellcode:
FILE_ATTRIBUTE_TEMPORARY
Flag. Solange dem System genug freier physikalischer zur Verfügung steht und nichts ausgelagert werden muss, befindet sich die Datei tatsächlich komplett im RAM.

Zitat:

Zitat von Benmik (Beitrag 1353138)
Der Taskmanager - der ja augenscheinlich erschütternd unzuverlässig ist - zeigt nun statt 500 MB nur noch 180 MB an.

Wie gesagt, der Taskmanager zeigt mit dem WorkingSet nur den momentan tatsächlich physikalisch verwendeten Speicher des Prozesses an. Also exklusive ausgelagerter Pages. Daten, die du in den FileCache lädst sind darin auch nicht enthalten.

Zitat:

Zitat von Benmik (Beitrag 1353138)
Die ganze Geschichte läuft aber merklich langsamer ab als mit den direkt gespeicherten Bitmaps. Die Erstellung der Bitmaps/Streams ist in asychrone Threads ausgelagert, auch damit scheint es Probleme zu geben, das Programm hängt sich bei forcierter Belastung auf ("forcierte Belastung" heißt, ich halte die Weiter-Taste gedrückt und nudle 100 Bilder durch, dadurch müssen pausenlos Bilder geladen, gelesen und verworfen werden). Hab's jetzt aber noch nicht richtig handoptimiert, mal sehen.

Tatsächlich haben auch diese speziellen Streams einen Performance Nachteil gegenüber der direkten Speicherung im RAM. Das liegt mitunter daran, dass Windows die Dateien im Filesystem "simuliert" und auch Eigenschaften wie die letzte Zugriffszeit etc. jeweils aktualisiert. Normalen FileStreams sind sie aber trotzdem noch weit überlegen.

Optimieren könntest du noch, indem du nach dem Laden des ersten Bildes im Hintergrund schon anfängst Nummer zwei (und evtl. danach noch Nummer 3) zu laden, etc. Sind sicher noch einige Verbesserungen drinnen.

t.roller 9. Nov 2016 19:00

AW: Speicherlast des Programms mittels GetMemoryManagerState - komischer Wert
 
Liste der Anhänge anzeigen (Anzahl: 1)
Schon mal an RAMDisk gedacht?

Benmik 9. Nov 2016 19:07

AW: Speicherlast des Programms mittels GetMemoryManagerState - komischer Wert
 
Zitat:

Zitat von Zacherl (Beitrag 1353166)
Optimieren könntest du noch, indem du nach dem Laden des ersten Bildes im Hintergrund schon anfängst Nummer zwei (und evtl. danach noch Nummer 3) zu laden...

Das geschieht ja mit den asynchronen Threads, und da ich einen Threadpool mit
Delphi-Quellcode:
MaxThreads := 2 * System.CPUCount;
verwende, werden sogar alle BMP gleichzeitig geladen (und geht bei einer SSD richtig schnell, bei einer normalen Festplatte ist das eher kontraproduktiv). Dieses Laden geht bei Bitmaps viel schneller. Aber daran könnte ich wohl noch schrauben.

RAMDisk sieht sehr interessant aus, ist für meine Anwendung aber eher nichts (müsste erst eingerichtet werden, der Platzbedarf ändert sich dauernd mit den Einstellungen und der Größe der Bitmaps usw.). Aber für andere Sachen wäre das vielleicht wirklich interessant!

PS:
Ich habe jetzt in einer Schleife 100 Bilder einzeln geladen, dekomprimiert, in den Stream geladen und wieder freigegeben. Das dauerte etwa 37 Sekunden, also eine drittel Sekunde pro Bild. Das Wiederauslesen des Streams und Laden in ein Bitmap verlängerte die Prozedur um 5 Sekunden, also eine Zwanzigstelsekunde je Bitmap. Da scheint mir kaum Potenzial für Verbesserungen zu sein. Ich gucke mir nochmal das Management der Threads an.

PPS:
Das RAM-Disk-Tool ImDisk findet man jetzt hier, oder besser noch bei Sourceforge mit Konfigurator.

PPPS:
Von der RAM-Disk aus läuft der Prozess mit den 100 Bildern in 35 (!) Sekunden. Das scheint mir kein echtes Optimierungspotenzial.

Benmik 12. Nov 2016 20:01

AW: Speicherlast des Programms mittels GetMemoryManagerState - komischer Wert
 
Ich habe jetzt eine Reihe von Versuchen durchgeführt, die die alte (Speichern von TBitmap) mit der neuen (TMemoryFileStream) Methode vergleicht, mit einem etwas zwiespältigen Ergebnis.
  • Mit TMemoryFileStream kann ich abenteuerliche Werte für die Anzahl der vorzuladenden Bilder eingeben, zum Beispiel 30 vor und 30 zurück, ohne das es knallt. Gut!!
  • Als Extremwert habe ich 99 vorzuladende Bilder eingestellt (Maximalwert). Hierbei belegt der Filecache (ich habe 8 GB RAM) laut Ressourcenmonitor etwa 6 GB, uneingeschränkt frei noch 80 MB. Das ist eindrucksvoll.
  • Das Laden und Anzeigen der Bilder dauert ziemlich zuverlässig doppelt so lange wie mit TBitmaps.
  • Dies liegt am Speichern im TMemoryFileStream. Das Laden der JPG von der SSD ist vernachlässigbar, 15 ms. Das Dekodieren der Bitmaps aus den JPG dauert sehr einheitlich etwa 170 ms.
  • Bei Methode 2 kommt nun noch das Speichern im Stream dazu. Das dauert 140 bis 170 ms, dazwischen aber immer wieder über 500 ms.
  • Das Laden mit den Bitmaps wirkt schneller und spritziger, reagibler. Das "Durchnudeln" mit gedrückter Vorwärtstaste geht richtig zügig.
Jetzt kann ich mich entscheiden, was ich will, Leistung oder Robustheit. Vermutlich ist Letzteres wichtiger.

Zacherl 13. Nov 2016 19:38

AW: Speicherlast des Programms mittels GetMemoryManagerState - komischer Wert
 
Mir kommt da grade noch eine Idee:

Dein größtes Problem ist ja erstmal die 2GiB Grenze des virtuellen Adressraums. Du könntest dir eine weitere (Stream-)Klasse basteln, in der du die Daten als Memory Mapped File (MSDN-Library durchsuchenCreateFileMapping, MSDN-Library durchsuchenMapViewOfFile) speicherst. Solange mindestens ein Handle auf das MMF offen ist, wird es vom System nicht gelöscht, selbst dann, wenn du den Dateiinhalt mit MSDN-Library durchsuchenUnmapViewOfFile wieder aus deinem Prozessspeicher entfernst.
Wenn du jetzt jeweils nur das aktuell geöffnete Bild mappst, hast du auch immer nur einen großen zusammenhängenden Speicherblock in deinem virtuellen Adressraum liegen und würdest zumindest diese Ursache für eine
Delphi-Quellcode:
EOutOfMemory
Exception eliminieren. Sollte dein physikalischer RAM irgendwann nicht mehr ausreichen, fängt Windows halt an RAM in die Auslagerungsdatei auszulagern.

Zu bedenken ist hierbei allerdings, dass du mit dieser Methode natürlich die Leistung des ganzen System beeinträchtigen kannst, wenn du den kompletten RAM mit deinen MMFs füllst. Wobei selbst hier der Overhead nicht allzu fatal sein sollte.


Alle Zeitangaben in WEZ +1. Es ist jetzt 10:28 Uhr.
Seite 1 von 2  1 2      

Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz