Heap-Fragmentierung (Pascal)
Heap-Fragmentierung (Pascal)
Hallo!
Lange Zeit war das kein Thema für mich, aber in letzter Zeit ist es mir bewusst geworden:
Wenn man in Pascal mit GetMem und FreeMem hantiert, dann kann es schnell passieren,
dass der Heap fragmentiert wird, und obwohl insgesamt z.B. 64K noch frei wären, lässt
sich dieser Speicherplatz dann nicht mehr am Stück nutzen.
Dieses Problem stellt sich mir nun inbesondere aufgrund der Überlegung, ob ich für ZSM
noch einen Tracker schreibe. Den brauche ich nicht unbedingt, ich kann von einem anderen
Tracker konvertieren, aber es wäre auch so ein kleiner Wunsch der in Erfüllung gehen würde.
Nun hätte ich dabei aber das Problem, wenn man Samples reinlädt und der User sich
zwischendurch entscheidet, diese durch andere auszutauschen. Der Heap würde
fragmentiert werden.
Hat jemand einen Ansatz, wie man das vermeiden könnte?
Es wäre nämlich auch für ein Spiel praktikabel, wenn man für verschiedene Levels
unterschiedliche Grafik, Musik und Sounds laden möchte.
Ich erinnere mich, dass DOSferatu einen Speichermanager geschrieben hatte, der
zu Anfang den gesamten verfügbaren Speicher reserviert und dann selbst organisiert.
Vielleicht wäre das schon die Lösung.
Ich habe mich damit nur nicht länger befasst weil mir der Speicherumgang mit
GetMem und FreeMem so vertraut war und ich bisher keine Probleme mit
Heap-Fragmentierung hatte.
Lange Zeit war das kein Thema für mich, aber in letzter Zeit ist es mir bewusst geworden:
Wenn man in Pascal mit GetMem und FreeMem hantiert, dann kann es schnell passieren,
dass der Heap fragmentiert wird, und obwohl insgesamt z.B. 64K noch frei wären, lässt
sich dieser Speicherplatz dann nicht mehr am Stück nutzen.
Dieses Problem stellt sich mir nun inbesondere aufgrund der Überlegung, ob ich für ZSM
noch einen Tracker schreibe. Den brauche ich nicht unbedingt, ich kann von einem anderen
Tracker konvertieren, aber es wäre auch so ein kleiner Wunsch der in Erfüllung gehen würde.
Nun hätte ich dabei aber das Problem, wenn man Samples reinlädt und der User sich
zwischendurch entscheidet, diese durch andere auszutauschen. Der Heap würde
fragmentiert werden.
Hat jemand einen Ansatz, wie man das vermeiden könnte?
Es wäre nämlich auch für ein Spiel praktikabel, wenn man für verschiedene Levels
unterschiedliche Grafik, Musik und Sounds laden möchte.
Ich erinnere mich, dass DOSferatu einen Speichermanager geschrieben hatte, der
zu Anfang den gesamten verfügbaren Speicher reserviert und dann selbst organisiert.
Vielleicht wäre das schon die Lösung.
Ich habe mich damit nur nicht länger befasst weil mir der Speicherumgang mit
GetMem und FreeMem so vertraut war und ich bisher keine Probleme mit
Heap-Fragmentierung hatte.
mov ax, 13h
int 10h
while vorne_frei do vor;
int 10h
while vorne_frei do vor;
Re: Heap-Fragmentierung (Pascal)
Hallo, Zatzen!zatzen hat geschrieben: Ich erinnere mich, dass DOSferatu einen Speichermanager geschrieben hatte, der
zu Anfang den gesamten verfügbaren Speicher reserviert und dann selbst organisiert.
Vielleicht wäre das schon die Lösung.
Ich habe mich damit nur nicht länger befasst weil mir der Speicherumgang mit
GetMem und FreeMem so vertraut war und ich bisher keine Probleme mit
Heap-Fragmentierung hatte.
Ja, ich nutze meinen Speichermanager mittlerweile in quasi JEDEM meiner Projekte/Programme.
Es ist schon ein beruhigendes Gefühl, nicht mehr ständig über Speicherreservierung nachdenken zu müssen - alles was zählt, ist, daß alles zusammengenommen den gesamten freien Speicher nicht übersteigen darf (logisch). Wobei ich sagen muß, daß ich in manchen meiner Projekte sogar noch einen Schritt weiter gegangen bin und den Kram dermaßen dynamisch gemacht habe, daß man denken könnte, ich will ne Windows-GUI bauen:
Das ganze war dreistufig, nutzte Heap, XMS und dann auch Festplatte als Auslagerungsspeicher. Das brauchte ich z.B. für Füllfunktionen für große Auflösungen oder für das Retten von Fensterhintergründen bei der GUI meines Grafik/Level/Sprite-Tools.
Naja, an sich ist die Idee hinter meinem Speichermanager recht simpel. Vor jedem reservierten Speicherbereich gibt es einen Header, der unter anderem die Größe, sowie die absolute Position des Pointers enthält (und noch Typen usw...) Das ist wichtig, weil, wenn die Größe später geändert wird oder Speicherbereiche DAVOR vergrößert werden, dann ändert sich ja der Anfang des Speicherbereichs - dazu hat man die Position des Pointers - denn der Speichermanager ändert dann auch automatisch den Pointer - so daß er danach auf die neue Position zeigt.
Wird nun die Größe eines Speicherbereichs geändert, so werden aus allen nachfolgenden "Header" die Pointerpositionen gelesen und in die Pointer selbst werden die verschobenen Positionen geschrieben. (Die Header VOR dem geänderten Speicherbereich bleiben selbstverständlich unbeeinflußt.)
Natürlich werden bei Änderung eines Speicherbereichs, der NICHT der letzte in der "Kette" ist, die ganzen nachfolgenden Speicherbereiche noch umkopiert (verschoben). D.h. es ist natürlich keine gute Idee, andauernd die Größen von Speicherbereichen innerhalb von sehr zeitkritischen Abläufen zu ändern - aber das sollte ja klar sein.
Für XMS habe ich dann noch einmal das gleiche gemacht - da werden als "Pointer" einfach Longint-Variablen benutzt.
Und ja - ich finde dynamische Speichernutzung sehr nützlich - und für größere Projekte, gerade im Heap-Bereich, nahezu unvermeidlich.
Re: Heap-Fragmentierung (Pascal)
1.zatzen hat geschrieben:Hallo!
...
Nun hätte ich dabei aber das Problem, wenn man Samples reinlädt und der User sich
zwischendurch entscheidet, diese durch andere auszutauschen. Der Heap würde
fragmentiert werden.
Hat jemand einen Ansatz, wie man das vermeiden könnte? ...
Eigenen Speichermanager schreiben, wenn man DosFeratu heißt.
2.
Eingriff in die Speicherverwaltung von TP (mit mir nicht.)
3.
Verwendung von Borland Pascal 7.0 anstelle von TP 7.0. Dann müsstest Du statt max. 640kb Heap knapp 16MB Heap haben (BP 7.0 habe ich nicht).
4.
Brute Force bei TP 7.0: 512 kb XMS (z.B.) allozieren; tritt dann die Situation auf, dass der Heap zu fragmentiert ist, aber dennoch theoretisch genügend Heap frei ist (MemAvail), dann alle Heap – Bereiche in den Xms kopieren, alle Heap-Bereiche freigeben (FreeMem,Dispose), alle Heap-Bereiche neu anlegen und aus dem XMS wieder zurück in den Heap kopieren. Du musst natürlich bei der Ermittlung des Gesamtspeicherbedarfs berücksichtigen, dass TP den Heap immer in 8-byte-Happen verteilt. Ein Speicherbereich mit 33 byte (z.B. GetMem (MyPtr, 33)) verbraucht daher an Heap immer 40 byte usw.
5.
Im Game würde ich bei Level-Wechsel immer alle level-relevanten Speicherbereiche am Level – Ende freigeben und bei Beginn des neuen Levels komplett neu anlegen. Dann hast Du auch keine Fragmentierung.Es wäre nämlich auch für ein Spiel praktikabel, wenn man für verschiedene Levels
unterschiedliche Grafik, Musik und Sounds laden möchte.
Re: Heap-Fragmentierung (Pascal)
Abgesehen von einem Speichermanager erscheint mir die Brute Force Variante mir am sympathischsten.
Es wäre ja quasi eine Defragmentierung. Nur würde ich den XMS nicht bemühen wollen,
sondern für meinen eventuellen Tracker lieber eine Auslagerungsdatei benutzen.
Wie schnell oder langsam das wäre müsste man sehen.
Wobei eigentlich, der Tracker ist ja ein Nutzprogramm, da kann man ruhig
XMS voraussetzen, es könnte sowieso etwas eng werden auf dem Heap wegen
der ungepackten Patterns.
Der Hauptsinn für einen ZSM Tracker wäre, dass man direkt hören kann, wie
es hinterher klingt. Die 4 Bit Samples können im ungünstigsten Fall schonmal
ein Sample etwas entstellen, zudem wird ohne Interpolation gemischt, und
ich verwende den anderen Tracker, von dem ich bislang konvertiere, mit Interpolation (GUS).
Aber der Aufwand lohnt sich wohl nicht.
Jedenfalls nicht heute - als ich vor 22 Jahren meinen simplen Adlib-Tracker gemacht
hatte haben doch tatsächlich ein paar Freunde damit etwas komponiert.
Danke jedenfalls Euch für die Antworten. Ich sehe das jetzt deutlich klarer.
Es wäre ja quasi eine Defragmentierung. Nur würde ich den XMS nicht bemühen wollen,
sondern für meinen eventuellen Tracker lieber eine Auslagerungsdatei benutzen.
Wie schnell oder langsam das wäre müsste man sehen.
Wobei eigentlich, der Tracker ist ja ein Nutzprogramm, da kann man ruhig
XMS voraussetzen, es könnte sowieso etwas eng werden auf dem Heap wegen
der ungepackten Patterns.
Der Hauptsinn für einen ZSM Tracker wäre, dass man direkt hören kann, wie
es hinterher klingt. Die 4 Bit Samples können im ungünstigsten Fall schonmal
ein Sample etwas entstellen, zudem wird ohne Interpolation gemischt, und
ich verwende den anderen Tracker, von dem ich bislang konvertiere, mit Interpolation (GUS).
Aber der Aufwand lohnt sich wohl nicht.
Jedenfalls nicht heute - als ich vor 22 Jahren meinen simplen Adlib-Tracker gemacht
hatte haben doch tatsächlich ein paar Freunde damit etwas komponiert.
Danke jedenfalls Euch für die Antworten. Ich sehe das jetzt deutlich klarer.
mov ax, 13h
int 10h
while vorne_frei do vor;
int 10h
while vorne_frei do vor;
Re: Heap-Fragmentierung (Pascal)
Naja, wie ich bereits erwähnte: Das Ganze ist kein Hexenwerk, ich habe das damals in Pascal gemacht und nutze es immer noch. Ich habe ja geschrieben, wie mein Zeug da ungefähr funktioniert, damit Du vielleicht Deinen eigenen Speichermanager bauen kannst. (Klar - wer will schon fremden Code benutzen?)
Der Gedanke dahinter war nur. einen der möglichen Ansätze dazu zu erklären, so als Denkanstoß.
Der Gedanke dahinter war nur. einen der möglichen Ansätze dazu zu erklären, so als Denkanstoß.
- Thomas
- DOS-Kenner
- Beiträge: 426
- Registriert: Mi 22. Jun 2016, 12:29
- Wohnort: Nähe von Limburg / Lahn
Re: Heap-Fragmentierung (Pascal)
Hallo Zatzen.
Bist Du aber nicht, oder? ![Very Happy :-D](./images/smilies/icon_e_biggrin.gif)
Wie auch immer, könnte man den Tracker Mal sehen?
Ein leider irgendwie verschollener Freund von mir, mit dem ich auch immer Mal kleinere Spielchen in QBasic und später auch in Pascal gemacht habe, OK ER hat gemacht und ich habe gefordert, hatte auch Mal einen FM Tracker geschrieben. 9 FM Kanäle (AdLib halt) plus einen Sample Kanal für Effekte oder Drums. Damit habe ich auch fleißig getrackert seinerzeit. War auch so um die Zeit Rum...- als ich vor 22 Jahren meinen simplen Adlib-Tracker gemacht
hatte haben doch tatsächlich ein paar Freunde damit etwas komponiert.
![Shocked :shock:](./images/smilies/icon_eek.gif)
![Very Happy :-D](./images/smilies/icon_e_biggrin.gif)
Wie auch immer, könnte man den Tracker Mal sehen?
Ein bisschen DOS kann oft mehr als ein Haufen Fenster.
Gigabyte GA-586HX, P54C 100@75MHz, 24MB RAM, AVGA3-22-1M ISA, RTL8029AS PCI, Goldstar Prime 2 ISA, MA5ASOUND, Dreambl. X2 DB, HD 4x2GB, 48x CD, 3,5" Floppy, 2xRS232, 1xPar., PS/2 Maus
Gigabyte GA-586HX, P54C 100@75MHz, 24MB RAM, AVGA3-22-1M ISA, RTL8029AS PCI, Goldstar Prime 2 ISA, MA5ASOUND, Dreambl. X2 DB, HD 4x2GB, 48x CD, 3,5" Floppy, 2xRS232, 1xPar., PS/2 Maus
Re: Heap-Fragmentierung (Pascal)
Auf die schnelle mal ein Video von meinem zweiten FM-Tracker:
https://youtu.be/tn3VHysfGkM
Die Programme oder Codes (evtl. muss man etwas ändern damit es hier oder da läuft) müsste ich noch raussuchen.
https://youtu.be/tn3VHysfGkM
Die Programme oder Codes (evtl. muss man etwas ändern damit es hier oder da läuft) müsste ich noch raussuchen.
mov ax, 13h
int 10h
while vorne_frei do vor;
int 10h
while vorne_frei do vor;
- Thomas
- DOS-Kenner
- Beiträge: 426
- Registriert: Mi 22. Jun 2016, 12:29
- Wohnort: Nähe von Limburg / Lahn
Re: Heap-Fragmentierung (Pascal)
Hallo Zatzen. Der Frankus Tracker II ist genial.
Allein die Tatsache dass er unter QBasic läuft. Kommst Du da ganz ohne Call Absolute aus?
Allein die Tatsache dass er unter QBasic läuft. Kommst Du da ganz ohne Call Absolute aus?
Ein bisschen DOS kann oft mehr als ein Haufen Fenster.
Gigabyte GA-586HX, P54C 100@75MHz, 24MB RAM, AVGA3-22-1M ISA, RTL8029AS PCI, Goldstar Prime 2 ISA, MA5ASOUND, Dreambl. X2 DB, HD 4x2GB, 48x CD, 3,5" Floppy, 2xRS232, 1xPar., PS/2 Maus
Gigabyte GA-586HX, P54C 100@75MHz, 24MB RAM, AVGA3-22-1M ISA, RTL8029AS PCI, Goldstar Prime 2 ISA, MA5ASOUND, Dreambl. X2 DB, HD 4x2GB, 48x CD, 3,5" Floppy, 2xRS232, 1xPar., PS/2 Maus
Re: Heap-Fragmentierung (Pascal)
Hallo Thomas!
Ja, ist alles normaler Coden bloß eben mit ein paar Port-Befehlen.
Das kritische an der Sache ist, dass ich zum timen keinen Interrupt verwendet habe sondern den Timer der Soundblaster, und das ist nicht mit jeder Soundblaster-kompatiblen Karte ohne weiteres kompatibel.
Noch ein Problem war, dass ein plötzliches Umprogrammieren der Parameter eines Operator-Paars zu Störgeräuschen führte, weshalb man auf jedem Kanal je nur einen festen Klang einstellen kann.
Ja, ist alles normaler Coden bloß eben mit ein paar Port-Befehlen.
Das kritische an der Sache ist, dass ich zum timen keinen Interrupt verwendet habe sondern den Timer der Soundblaster, und das ist nicht mit jeder Soundblaster-kompatiblen Karte ohne weiteres kompatibel.
Noch ein Problem war, dass ein plötzliches Umprogrammieren der Parameter eines Operator-Paars zu Störgeräuschen führte, weshalb man auf jedem Kanal je nur einen festen Klang einstellen kann.
mov ax, 13h
int 10h
while vorne_frei do vor;
int 10h
while vorne_frei do vor;
- Thomas
- DOS-Kenner
- Beiträge: 426
- Registriert: Mi 22. Jun 2016, 12:29
- Wohnort: Nähe von Limburg / Lahn
Re: Heap-Fragmentierung (Pascal)
Och, das mit dem Timing ist in BASIC easy going. Siehe meine Fragen zu meinem Portierungsprojekt. Du schreibst einfach die gesamte Player Routine ins Hauptprogramm mit einem Label und springst sie mit
ON TIMER (1) GOSUB Label
TIMER ON
An. Vorher natürlich den Timer via OUT Anweisung auf die Ports $43 und $40 entsprechend programmieren. 50Hz sind 911 Ticks, meine ich. Am Ende der Player Routine das RETURN nicht vergessen.
Mit meinem Port bin ich immer noch nicht weiter... Aber dass steht alles im entsprechenden Thread.
Den Timer den du verwendet hast hat nicht jede Soundkarte? Nehme an du meinst einen der zwei Timer der Yamaha OPL Chips. Den haben alle mit OPL2/3 Chip oder den Kopien. Die Clone vielleicht nicht, weiß ich nicht, sollten aber auf kompatibilitätsgründen nicht darauf verzichten.
ON TIMER (1) GOSUB Label
TIMER ON
An. Vorher natürlich den Timer via OUT Anweisung auf die Ports $43 und $40 entsprechend programmieren. 50Hz sind 911 Ticks, meine ich. Am Ende der Player Routine das RETURN nicht vergessen.
Mit meinem Port bin ich immer noch nicht weiter... Aber dass steht alles im entsprechenden Thread.
Den Timer den du verwendet hast hat nicht jede Soundkarte? Nehme an du meinst einen der zwei Timer der Yamaha OPL Chips. Den haben alle mit OPL2/3 Chip oder den Kopien. Die Clone vielleicht nicht, weiß ich nicht, sollten aber auf kompatibilitätsgründen nicht darauf verzichten.
Ein bisschen DOS kann oft mehr als ein Haufen Fenster.
Gigabyte GA-586HX, P54C 100@75MHz, 24MB RAM, AVGA3-22-1M ISA, RTL8029AS PCI, Goldstar Prime 2 ISA, MA5ASOUND, Dreambl. X2 DB, HD 4x2GB, 48x CD, 3,5" Floppy, 2xRS232, 1xPar., PS/2 Maus
Gigabyte GA-586HX, P54C 100@75MHz, 24MB RAM, AVGA3-22-1M ISA, RTL8029AS PCI, Goldstar Prime 2 ISA, MA5ASOUND, Dreambl. X2 DB, HD 4x2GB, 48x CD, 3,5" Floppy, 2xRS232, 1xPar., PS/2 Maus
Re: Heap-Fragmentierung (Pascal)
Es ist lange her. Für den Tracker war ein Abspielen im Hintergrund nicht nötig.
Später habe ich mir in Pascal ein TSR geschrieben das die Musik spielte, funktionierte mehr oder weniger.
Mit ON TIMER habe ich nie etwas gemacht, käme jetzt aber auch nicht mehr in Frage für mich, da ich ja auf Pascal umgestiegen bin. Aber vielleicht wenn mal jemand Interesse an dem Tracker hat, könnte man den Code entsprechend ändern damit das Programm überall funktioniert.
Vielleicht nichts neues für Dich, ich erinnere mich nur gerade daran, dass man den Uhrzeit-Counter in der neuen Timer-Routine korrekt updaten sollte, wenn man nicht will dass nach der Ausführung des Programms die Uhrzeit nicht mehr stimmt.
Aktuell habe ich ein anderes Problem. Ich habe Abspielroutinen für Sample-basierte Musik, das wird dann in Puffern gerechnet und das ganze mit Double-Buffering abgespielt. Jetzt fällt mir nichts besseres ein als die Anwendung/das Spiel die/das diese Musik-Routinen verwendet, mit dem Puffer zu timen, also immer auf den Interrupt zu warten, der kommt, wenn einer der beiden Puffer durchgespielt ist. Es werden dann also während ein Puffer spielt die Sounddaten für den nächsten berechnet, wie auch alle anderen Daten wie z.B. Grafik. Das Problem ist, dass man dabei Rechenleistung verschenkt und zudem eine feste Framerate hat. Ich habe mich dazu ausführlich schon mit DOSferatu unterhalten, aber ich tue mich schwer mit dem Konzept der variablen Framerate zugunsten von Vermeidung von Stottern.
Später habe ich mir in Pascal ein TSR geschrieben das die Musik spielte, funktionierte mehr oder weniger.
Mit ON TIMER habe ich nie etwas gemacht, käme jetzt aber auch nicht mehr in Frage für mich, da ich ja auf Pascal umgestiegen bin. Aber vielleicht wenn mal jemand Interesse an dem Tracker hat, könnte man den Code entsprechend ändern damit das Programm überall funktioniert.
Vielleicht nichts neues für Dich, ich erinnere mich nur gerade daran, dass man den Uhrzeit-Counter in der neuen Timer-Routine korrekt updaten sollte, wenn man nicht will dass nach der Ausführung des Programms die Uhrzeit nicht mehr stimmt.
Aktuell habe ich ein anderes Problem. Ich habe Abspielroutinen für Sample-basierte Musik, das wird dann in Puffern gerechnet und das ganze mit Double-Buffering abgespielt. Jetzt fällt mir nichts besseres ein als die Anwendung/das Spiel die/das diese Musik-Routinen verwendet, mit dem Puffer zu timen, also immer auf den Interrupt zu warten, der kommt, wenn einer der beiden Puffer durchgespielt ist. Es werden dann also während ein Puffer spielt die Sounddaten für den nächsten berechnet, wie auch alle anderen Daten wie z.B. Grafik. Das Problem ist, dass man dabei Rechenleistung verschenkt und zudem eine feste Framerate hat. Ich habe mich dazu ausführlich schon mit DOSferatu unterhalten, aber ich tue mich schwer mit dem Konzept der variablen Framerate zugunsten von Vermeidung von Stottern.
mov ax, 13h
int 10h
while vorne_frei do vor;
int 10h
while vorne_frei do vor;
- Thomas
- DOS-Kenner
- Beiträge: 426
- Registriert: Mi 22. Jun 2016, 12:29
- Wohnort: Nähe von Limburg / Lahn
Re: Heap-Fragmentierung (Pascal)
Das mit den Counter ist mir bekannt. Einfach vor Ende des Programms oder bei einem Laufzeitfehler NACH der Reprogrammierung des Timers, diesen wieder auf 18.2 setzen und mittels der RTC die Uhrzeit anpassen. Sollte theoretisch mit SetTime in Pascal gehen. Damit beschäftige ich mich aber erst wenn der Player Mal das abspielt was er soll.
Bei Deinem Problem habe ich so ad hoc auch keine Lösung. Bei meinem Spiel war das alles mehr oder weniger dilettantisch gelöst. DirectQB setzt den Timer entsprechend der gewünschten Framerate und daran habe ich den Player angepasst. Sample Ausgabe dieser Lib ist für mich nicht transparent da in komplexen 386 ASM geschrieben. Jedenfalls habe ich keine nennenswerten Ruckler außer dass es insgesamt auf dem 486er zu langsam läuft.
Aber das ist eine Basic library/ein Basic Spiel und auch kein Sample Mod Player sondern FM.
Bei Deinem Problem habe ich so ad hoc auch keine Lösung. Bei meinem Spiel war das alles mehr oder weniger dilettantisch gelöst. DirectQB setzt den Timer entsprechend der gewünschten Framerate und daran habe ich den Player angepasst. Sample Ausgabe dieser Lib ist für mich nicht transparent da in komplexen 386 ASM geschrieben. Jedenfalls habe ich keine nennenswerten Ruckler außer dass es insgesamt auf dem 486er zu langsam läuft.
Aber das ist eine Basic library/ein Basic Spiel und auch kein Sample Mod Player sondern FM.
Ein bisschen DOS kann oft mehr als ein Haufen Fenster.
Gigabyte GA-586HX, P54C 100@75MHz, 24MB RAM, AVGA3-22-1M ISA, RTL8029AS PCI, Goldstar Prime 2 ISA, MA5ASOUND, Dreambl. X2 DB, HD 4x2GB, 48x CD, 3,5" Floppy, 2xRS232, 1xPar., PS/2 Maus
Gigabyte GA-586HX, P54C 100@75MHz, 24MB RAM, AVGA3-22-1M ISA, RTL8029AS PCI, Goldstar Prime 2 ISA, MA5ASOUND, Dreambl. X2 DB, HD 4x2GB, 48x CD, 3,5" Floppy, 2xRS232, 1xPar., PS/2 Maus