Was ist eine sichere maximale Stackgröße oder Wie wird die Stacknutzung gemessen?

8

Ich habe eine App mit einer Anzahl von Worker-Threads, einen für jeden Kern. Auf einer modernen 8-Kern-Maschine habe ich 8 dieser Threads. Meine App lädt viele Plugins, die auch ihre eigenen Worker-Threads haben. Da die App riesige Speicherblöcke verwendet (Fotos, zB 200 MB) habe ich ein Speicherfragmentierungsproblem. Das Problem ist, dass jeder Thread den Adressraum {$ MAXSTACKSIZE ...} zuweist. Es verwendet nicht den physischen Speicher, sondern den Adressraum. Ich habe die MAXSTACKSIZE von 1MB auf 128KB reduziert und es scheint zu funktionieren, aber ich mache es jetzt nicht, wenn ich fast am Limit bin. Gibt es eine Möglichkeit zu messen, wie viel Stack tatsächlich genutzt wird?

    
Steffen Binas 27.05.2011, 09:02
quelle

6 Antworten

11

Verwenden Sie dies, um die Speichermenge zu berechnen, die für den Stack des aktuellen Threads festgelegt wurde:

%Vor%

Eine andere Idee, die ich nicht habe.

    
opc0de 27.05.2011, 09:26
quelle
7

Der Vollständigkeit halber füge ich eine Version der Funktion CommittedStackSize hinzu, die in opc0des Antwort bereitgestellt wird für Bestimmen der Menge des verwendeten Stacks , die sowohl für x86 32- als auch für 64-Bit-Versionen von Windows funktioniert (die Funktion von opc0de ist nur für Win32).

Die Funktion von

opc0de fragt die Adresse der Basis des Stapels und die unterste festgeschriebene Stapelbasis von Thread-Informationsblock (TIB) . Es gibt zwei Unterschiede zwischen x86 und x64:

  • TIB wird durch das FS Segmentregister auf Win32, aber durch GS auf Win64 angezeigt (siehe hier )
  • Die absoluten Offsets von Elementen in der Struktur unterscheiden sich (hauptsächlich, weil einige Elemente Zeiger sind, d.h. 4 Bytes bzw. 8 Bytes auf Win32 / 64)

Beachten Sie außerdem, dass es einen kleinen Unterschied im BASM-Code gibt, denn auf x64 ist abs erforderlich, damit der Assembler einen absoluten Offset vom Segment-Register verwendet.

Daher sieht eine Version, die sowohl mit Win32 als auch Win64 funktioniert, folgendermaßen aus:

%Vor%     
PhiS 19.01.2014 17:06
quelle
3

Ich erinnere mich, dass ich vor einigen Jahren den gesamten verfügbaren Stapelplatz mit Nullen gefüllt und die zusammenhängenden Nullen auf Deinit gezählt habe, beginnend am Ende. Dies ergab eine gute "High Watermark", vorausgesetzt, Sie senden Ihre App für Probe-Tests auf Herz und Nieren.

Ich werde den Code ausgraben, wenn ich nicht mobil bin.

Update: OK das Prinzip wird in diesem (alten) Code demonstriert:

%Vor%

(Aus Ссылка )

Ich erinnere mich schwach daran, zu dieser Zeit mit Kim Kokkonen gearbeitet zu haben, und ich denke, der Originalcode stammt von ihm.

Das Gute an diesem Ansatz ist, dass Sie während des Programmlaufs keine Leistungseinbußen und keinen Profiling-Vorgang ausführen müssen. Nur beim Herunterfahren verschlingt der Schleifen-bis-geändert-Wert-gefunden-Code CPU-Zyklen. (Wir haben das später in der Baugruppe codiert.)

    
TheBlastOne 27.05.2011 09:10
quelle
1
___ qstnhdr ___ Was ist eine sichere maximale Stackgröße oder Wie wird die Stacknutzung gemessen? ___ answer6150128 ___

Ich erinnere mich, dass ich vor einigen Jahren den gesamten verfügbaren Stapelplatz mit Nullen gefüllt und die zusammenhängenden Nullen auf Deinit gezählt habe, beginnend am Ende. Dies ergab eine gute "High Watermark", vorausgesetzt, Sie senden Ihre App für Probe-Tests auf Herz und Nieren.

Ich werde den Code ausgraben, wenn ich nicht mobil bin.

Update: OK das Prinzip wird in diesem (alten) Code demonstriert:

%Vor%

(Aus Ссылка )

Ich erinnere mich schwach daran, zu dieser Zeit mit Kim Kokkonen gearbeitet zu haben, und ich denke, der Originalcode stammt von ihm.

Das Gute an diesem Ansatz ist, dass Sie während des Programmlaufs keine Leistungseinbußen und keinen Profiling-Vorgang ausführen müssen. Nur beim Herunterfahren verschlingt der Schleifen-bis-geändert-Wert-gefunden-Code CPU-Zyklen. (Wir haben das später in der Baugruppe codiert.)

    
___ qstntxt ___

Ich habe eine App mit einer Anzahl von Worker-Threads, einen für jeden Kern. Auf einer modernen 8-Kern-Maschine habe ich 8 dieser Threads. Meine App lädt viele Plugins, die auch ihre eigenen Worker-Threads haben. Da die App riesige Speicherblöcke verwendet (Fotos, zB 200 MB) habe ich ein Speicherfragmentierungsproblem. Das Problem ist, dass jeder Thread den Adressraum {$ MAXSTACKSIZE ...} zuweist. Es verwendet nicht den physischen Speicher, sondern den Adressraum. Ich habe die MAXSTACKSIZE von 1MB auf 128KB reduziert und es scheint zu funktionieren, aber ich mache es jetzt nicht, wenn ich fast am Limit bin. Gibt es eine Möglichkeit zu messen, wie viel Stack tatsächlich genutzt wird?

    
___ antwort6150362 ___

Selbst wenn alle 8 Threads ihrem 1-MB-Stack nahe kommen würden, sind das nur 8 MB virtueller Speicher. IIRC, die Standard-Anfangs-Stack-Größe für Threads ist 64 KB und erhöht sich bei Seitenfehlern, wenn das Limit des Prozess-Thread-Stacks nicht erreicht wird. An diesem Punkt nehme ich an, dass Ihr Prozess mit einem 'Stack overflow' messageBox gestoppt wird: (

Ich befürchte, dass die Reduzierung des Stack-Limits $ MAXSTACKSIZE das Fragmentierungs- / Paging-Problem nicht wesentlich verringert. Du benötigst mehr RAM, damit das Resident-Seitenset deiner Mega-Foto-App größer ist & amp; so dass die Prügel reduziert werden.

Wie viele Threads gibt es insgesamt im Durchschnitt in Ihrem Prozess? Task-Manager kann dies zeigen.

Rgds, Martin

    
___ answer6150054 ___

Obwohl ich sicher bin, dass Sie die Thread-Stackgröße in Ihrer App reduzieren können, glaube ich nicht, dass damit die Ursache des Problems behoben wird. Sie verwenden jetzt eine 8-Kern-Maschine, aber was passiert bei einem 16-Kern oder einem 32-Kern usw.?

Mit 32 Bit Delphi haben Sie einen maximalen Adressraum von 4 GB und das begrenzt Sie in gewissem Maße. Möglicherweise müssen Sie für einige oder alle Ihre Threads kleinere Stacks verwenden, aber Sie werden weiterhin Probleme auf einer Maschine haben, die groß genug ist.

Wenn Sie Ihre App auf größeren Computern besser skalieren möchten, müssen Sie möglicherweise einen der folgenden Schritte ausführen:

  1. Vermeiden Sie es, wesentlich mehr Threads als Kerne zu erstellen. Verwenden Sie eine Thread-Pool-Architektur, die für Ihre Plug-Ins verfügbar ist. Ohne den Vorteil der .net-Umgebung, um dies zu vereinfachen, werden Sie am besten mit der Windows-Thread-Pool-API arbeiten. Das heißt, es muss eine gute Delphi-Wrapper verfügbar sein.
  2. Gehen Sie mit den Speicherzuweisungsmustern um. Wenn Ihre Threads zusammenhängende Blöcke im Bereich von 200 MB zuweisen, verursacht dies eine übermäßige Belastung für Ihren Zuordner. Ich habe festgestellt, dass es oft am besten ist, solch große Mengen an Speicher in kleineren Blöcken fester Größe zuzuordnen. Dieser Ansatz funktioniert bei den Fragmentierungsproblemen, denen Sie begegnen.
___ answer6150631 ___

Das Reduzieren von $ MAXSTACKSIZE funktioniert nicht, da Windows den Thread-Stack immer auf 1 MB (?) richtet.

Eine (mögliche?) Möglichkeit, Fragmentierung zu verhindern, ist das Reservieren (nicht Alloc!) virtueller Speicher (mit VirtualAlloc) vor dem Erstellen von Threads. Und loslassen, nachdem die Threads ausgeführt werden. Auf diese Weise kann Windows den reservierten Speicherplatz für die Threads nicht verwenden, so dass Sie über kontinuierlichen Speicher verfügen.

Oder Sie könnten Ihren eigenen Speichermanager für große Fotos erstellen: Reservieren Sie viel virtuellen Speicher und ordnen Sie Speicher aus diesem Pool manuell zu. (Sie müssen eine Liste von verwendetem und verwendetem Speicher selbst verwalten).

Zumindest ist das eine Theorie, weiß nicht, ob es wirklich funktioniert ...

    
___ tag123delphi ___ Delphi ist eine Sprache für die schnelle Entwicklung von nativen Windows-, macOS-, Linux-, iOS- und Android-Anwendungen mithilfe von Object Pascal. Der Name bezieht sich sowohl auf die Delphi-Sprache als auch auf deren Bibliotheken, Compiler und IDE, mit denen Delphi-Projekte bearbeitet und debuggt werden können. ___ tag123delphi2010 ___ Delphi 2010 ist eine spezielle Version von Delphi. Delphi 2010 wurde im August 2009 veröffentlicht und ist als eigenständiges Produkt oder als Teil von RAD Studio 2010 verfügbar. ___ tag123stack ___ Ein Stapel ist ein LIFO-abstrakter Datentyp und eine letzte Datenstruktur. Eine häufige Verwendung von Stapeln besteht darin, Unterroutinenargumente zu speichern und Adressen zurückzugeben. ___ answer6150294 ___

Verwenden Sie dies, um die Speichermenge zu berechnen, die für den Stack des aktuellen Threads festgelegt wurde:

%Vor%

Eine andere Idee, die ich nicht habe.

    
___ answer21219845 ___

Der Vollständigkeit halber füge ich eine Version der Funktion %code% hinzu, die in opc0des Antwort bereitgestellt wird für Bestimmen der Menge des verwendeten Stacks , die sowohl für x86 32- als auch für 64-Bit-Versionen von Windows funktioniert (die Funktion von opc0de ist nur für Win32).

Die Funktion von

opc0de fragt die Adresse der Basis des Stapels und die unterste festgeschriebene Stapelbasis von Thread-Informationsblock (TIB) . Es gibt zwei Unterschiede zwischen x86 und x64:

  • TIB wird durch das %code% Segmentregister auf Win32, aber durch %code% auf Win64 angezeigt (siehe hier )
  • Die absoluten Offsets von Elementen in der Struktur unterscheiden sich (hauptsächlich, weil einige Elemente Zeiger sind, d.h. 4 Bytes bzw. 8 Bytes auf Win32 / 64)

Beachten Sie außerdem, dass es einen kleinen Unterschied im BASM-Code gibt, denn auf x64 ist %code% erforderlich, damit der Assembler einen absoluten Offset vom Segment-Register verwendet.

Daher sieht eine Version, die sowohl mit Win32 als auch Win64 funktioniert, folgendermaßen aus:

%Vor%     
___
Martin James 27.05.2011 09:33
quelle
0

Obwohl ich sicher bin, dass Sie die Thread-Stackgröße in Ihrer App reduzieren können, glaube ich nicht, dass damit die Ursache des Problems behoben wird. Sie verwenden jetzt eine 8-Kern-Maschine, aber was passiert bei einem 16-Kern oder einem 32-Kern usw.?

Mit 32 Bit Delphi haben Sie einen maximalen Adressraum von 4 GB und das begrenzt Sie in gewissem Maße. Möglicherweise müssen Sie für einige oder alle Ihre Threads kleinere Stacks verwenden, aber Sie werden weiterhin Probleme auf einer Maschine haben, die groß genug ist.

Wenn Sie Ihre App auf größeren Computern besser skalieren möchten, müssen Sie möglicherweise einen der folgenden Schritte ausführen:

  1. Vermeiden Sie es, wesentlich mehr Threads als Kerne zu erstellen. Verwenden Sie eine Thread-Pool-Architektur, die für Ihre Plug-Ins verfügbar ist. Ohne den Vorteil der .net-Umgebung, um dies zu vereinfachen, werden Sie am besten mit der Windows-Thread-Pool-API arbeiten. Das heißt, es muss eine gute Delphi-Wrapper verfügbar sein.
  2. Gehen Sie mit den Speicherzuweisungsmustern um. Wenn Ihre Threads zusammenhängende Blöcke im Bereich von 200 MB zuweisen, verursacht dies eine übermäßige Belastung für Ihren Zuordner. Ich habe festgestellt, dass es oft am besten ist, solch große Mengen an Speicher in kleineren Blöcken fester Größe zuzuordnen. Dieser Ansatz funktioniert bei den Fragmentierungsproblemen, denen Sie begegnen.
David Heffernan 27.05.2011 09:04
quelle
0

Das Reduzieren von $ MAXSTACKSIZE funktioniert nicht, da Windows den Thread-Stack immer auf 1 MB (?) richtet.

Eine (mögliche?) Möglichkeit, Fragmentierung zu verhindern, ist das Reservieren (nicht Alloc!) virtueller Speicher (mit VirtualAlloc) vor dem Erstellen von Threads. Und loslassen, nachdem die Threads ausgeführt werden. Auf diese Weise kann Windows den reservierten Speicherplatz für die Threads nicht verwenden, so dass Sie über kontinuierlichen Speicher verfügen.

Oder Sie könnten Ihren eigenen Speichermanager für große Fotos erstellen: Reservieren Sie viel virtuellen Speicher und ordnen Sie Speicher aus diesem Pool manuell zu. (Sie müssen eine Liste von verwendetem und verwendetem Speicher selbst verwalten).

Zumindest ist das eine Theorie, weiß nicht, ob es wirklich funktioniert ...

    
André 27.05.2011 09:58
quelle

Tags und Links