Array lockere Bereichs- (Range-) check

Für Fragen rund um die Ide und zum Debugger
Warf
Beiträge: 1908
Registriert: Di 23. Sep 2014, 17:46
OS, Lazarus, FPC: Win10 | Linux
CPU-Target: x86_64

Re: Array lockere Bereichs- (Range-) check

Beitrag von Warf »

af0815 hat geschrieben:Ein klares nein. Nur weil ein Compiler nicht meckert ist der Code nicht in Ordnung. Wenn der Compiler nicht meckert heisst das nur das die Syntax stimmt - mehr nicht. Ob der Code korrekt ist, kann er nicht feststellen. Vor allen wenn man in Richtung Pointer oder Casts geht, kann der Compiler nicht alles prüfen.

Es ging grade um das CODE segment im speicher, welche größe das hat. Und das sollte man komplett dem Compiler überlassen, denn als programmierer hat man darauf nur geringen einfluss (Man kann versuchen per hand den assembly zu optimieren, aber ob sich das wirklich lohnt sei mal dahingestellt)

af0815 hat geschrieben:Auch die größe der Datentypen sind nicht in Stein gemeisselt. Es gibt Typen die haben immer die gleiche Größe, andere könne je nach Plattform sich ändern.

So sind die Aussagen zu einfach gehalten.


Das stimmt natürlich gibt es diverse Plattformabbhängige typen, richtig wäre alle Grunddatentypen (und damit auch kombinationstypen wie arrays und records) haben die selbe größe

Erwin
Beiträge: 286
Registriert: Mi 16. Sep 2009, 14:15
OS, Lazarus, FPC: Xubuntu 22.04 / x86_64_linux-gtk 2 / L 2.2.0 / FPC 3.2.2

Re: Array lockere Bereichs- (Range-) check

Beitrag von Erwin »

Das hört sich ja alles so an, als würde es nichts bringen, die Formel für den Speicherbereich etc. herauszufinden, bzw. das Ganze auszurechnen?
Irgendwie ernüchtern und erschreckend, dass es da keine klare Aufteilung oder dergleichen gibt. Dachte eigentlich immer, dass so lange es nur xx-RAM nutzt, dies dann überall gleich ist, und auch sonst nichts passieren kann?
Aber das würde erklären, wieso in der Vergangenheit paar Spiele auf meinen Rechner nicht liefen.
Lazarus 2.2.0 / FP 3.2.4

Benutzeravatar
kupferstecher
Beiträge: 418
Registriert: Do 17. Nov 2016, 11:52

Re: Array lockere Bereichs- (Range-) check

Beitrag von kupferstecher »

Erwin hat geschrieben:Das hört sich ja alles so an, als würde es nichts bringen, die Formel für den Speicherbereich etc. herauszufinden, bzw. das Ganze auszurechnen?

Auszurechnen sowieso nicht.
Man muss halt wissen, dass man den Stack nicht überstrapazieren sollte. Das heißt große statische Arrays sollten nicht lokal in Funktionen angelegt werden (sondern als globale Variablen oder als dynamisches Array). Dynamische Arrays werden auf dem Heap angelegt, ist also problemlos. Außerdem liegen lokale Shortstrings auf dem Stack, aber bei der PC-Programmierung verwendet man die ja sowieso nicht. Auf sonstige lokale Variablen wie Integer, Float usw. braucht man auf einem PC nicht zu achten, so viel kann man gar nicht tippen um den Stack zu gefährden. D.h. für das tägliche Programmieren hat das keine Relevanz.

Mathias
Beiträge: 6164
Registriert: Do 2. Jan 2014, 17:21
OS, Lazarus, FPC: Linux (die neusten Trunk)
CPU-Target: 64Bit
Wohnort: Schweiz

Re: Array lockere Bereichs- (Range-) check

Beitrag von Mathias »

Außerdem liegen lokale Shortstrings auf dem Stack, aber bei der PC-Programmierung verwendet man die ja sowieso nicht.

Das stimmt nicht, auch auf dem PC werden ShortString gebraucht.

ZB wen man eine alte Datei einlesen will. Oder wie willst du folgendes lösen ?

Code: Alles auswählen

type
  TData = record
    i: Int16;
    s: string[10];
  end;
var
  Data: TData;
  f: file of TData;
begin
  AssignFile(f, 'test.dat');
  Reset(f);
  Read(f, Data);
  CloseFile(f);
...
Mit Lazarus sehe ich grün
Mit Java und C/C++ sehe ich rot

Warf
Beiträge: 1908
Registriert: Di 23. Sep 2014, 17:46
OS, Lazarus, FPC: Win10 | Linux
CPU-Target: x86_64

Re: Array lockere Bereichs- (Range-) check

Beitrag von Warf »

Mathias hat geschrieben:Das stimmt nicht, auch auf dem PC werden ShortString gebraucht.

ZB wen man eine alte Datei einlesen will. Oder wie willst du folgendes lösen ?


Nicht nur alte dateien, ich verwende bis heute oft ShortStrings für binäre dateien zu schreiben, da ich sie das lesen und schreiben von Dateien extrem vereinfachen. Solang man garantieren kann das diese sich in einer gewissen größenordnung aufhalten. Name, Postleitzahlen, Addressen, Hausnummern, all das passt problemlos in shortstrings und das macht das speichern und laden extrem viel einfacher.

Selbes gilt natürlich auch für shared memory und sockets. Ich kann einen shortstring einfach direkt in einen TCP stream schreiben, und ihn auf meinem server lesen, mit nur 1 zeile code auf jeder seite

Mathias
Beiträge: 6164
Registriert: Do 2. Jan 2014, 17:21
OS, Lazarus, FPC: Linux (die neusten Trunk)
CPU-Target: 64Bit
Wohnort: Schweiz

Re: Array lockere Bereichs- (Range-) check

Beitrag von Mathias »

Nicht nur alte dateien, ich verwende bis heute oft ShortStrings für binäre dateien zu schreiben, da ich sie das lesen und schreiben von Dateien extrem vereinfachen.
Für etwas neues würde ich sowieso FileStream verwenden. Aber es stimmt, mit ShortString ist es sehr einfach, dafür verschwendet es Platz.
Mit Lazarus sehe ich grün
Mit Java und C/C++ sehe ich rot

Warf
Beiträge: 1908
Registriert: Di 23. Sep 2014, 17:46
OS, Lazarus, FPC: Win10 | Linux
CPU-Target: x86_64

Re: Array lockere Bereichs- (Range-) check

Beitrag von Warf »

Mathias hat geschrieben:Für etwas neues würde ich sowieso FileStream verwenden. Aber es stimmt, mit ShortString ist es sehr einfach, dafür verschwendet es Platz.


Ja aber man kann ja auch records mit shortstrings in Streams schreiben:

Code: Alles auswählen

Fs.Write(MyData, SizeOf(MyData))


Und die Platzverschwendung ist ja auch nicht so das sonderlich große Problem, da ein shortstring maximal 256 Zeichen lang werden kann, muss man schon ne menge daten haben damit das zum problem wird.
Im vergleich dazu, TLists verwenden geometrische Kapazitäten, das bedeutet wenn du ein element hinzufügst, das über die kapazität ausreicht, wird die kapazität um einen Faktor C vergrößert. Im worst case verwendet eine TList also C mal so viel speicher wie notwendig (ich glaube früher war C = 2, heute skaliert das je nach größe, also bei kleinen listen 2, bei großen listen 1,irgendwas). Wenn du deine Shortstrings in einer Liste organisierst kann es also gut sein das du durch die liste mehr platz verlierst als durch die Shortstrings

Mathias
Beiträge: 6164
Registriert: Do 2. Jan 2014, 17:21
OS, Lazarus, FPC: Linux (die neusten Trunk)
CPU-Target: 64Bit
Wohnort: Schweiz

Re: Array lockere Bereichs- (Range-) check

Beitrag von Mathias »

Ja aber man kann ja auch records mit shortstrings in Streams schreiben:

Wieso sollte dies nicht gehen, ein ShortString ist nichts anderes als eine statische Array.

Im vergleich dazu, TLists verwenden geometrische Kapazitäten, das bedeutet wenn du ein element hinzufügst, das über die kapazität ausreicht, wird ...

Muss man bei TList nicht den Speicher selber reservieren ?
Mit Lazarus sehe ich grün
Mit Java und C/C++ sehe ich rot

Warf
Beiträge: 1908
Registriert: Di 23. Sep 2014, 17:46
OS, Lazarus, FPC: Win10 | Linux
CPU-Target: x86_64

Re: Array lockere Bereichs- (Range-) check

Beitrag von Warf »

Mathias hat geschrieben:Wieso sollte dies nicht gehen, ein ShortString ist nichts anderes als eine statische Array.


Ja aber ich meine das ist immernoch einfacher als einen Record der z.B. einen Dynamischen string enthält reinzuschreiben, da man dann jedes feld per hand reinschreiben muss.

Muss man bei TList nicht den Speicher selber reservieren ?

Mit dem TList.Create erstellt man nur sozusagen das Kontrollobjekt, mit einem gewissen grundspeicher für die ersten paar elemente (keine ahnung wie viel, irgendwas zwischen 2-16 elemente oder so). Der speicher für die elemente wird dann beim add bzw remove von elementen angepasst (also wenn nach einem remove zu viel speicher frei ist, wird der verkleinert, wenn ein add mehr speicher verbraucht als aktuell vorhanden ist wird faktoriell vergrößert).

Die Idee dahinter ist, das speicher vergrößern und verkleinern echt langsam ist, und damit viele adds/removes nicht ewig viel Zeit brauchen wird also der speicher überapproximiert

Mathias
Beiträge: 6164
Registriert: Do 2. Jan 2014, 17:21
OS, Lazarus, FPC: Linux (die neusten Trunk)
CPU-Target: 64Bit
Wohnort: Schweiz

Re: Array lockere Bereichs- (Range-) check

Beitrag von Mathias »

Ja aber ich meine das ist immernoch einfacher als einen Record der z.B. einen Dynamischen string enthält reinzuschreiben, da man dann jedes feld per hand reinschreiben muss.
Das stimmt, bei dynamischen Sachen muss man immer zuerst die Länge schreiben.

Mit dem TList.Create erstellt man nur sozusagen das Kontrollobjekt, mit einem gewissen grundspeicher für die ersten paar elemente (keine ahnung wie viel, irgendwas zwischen 2-16 elemente oder so). Der speicher für die elemente wird dann beim add bzw remove von elementen angepasst (also wenn nach einem remove zu viel speicher frei ist, wird der verkleinert, wenn ein add mehr speicher verbraucht als aktuell vorhanden ist wird faktoriell vergrößert).
Im Prinzip könnte man die auch selbst mit einer verketteten Liste machen. Aber dies ist mehr Aufwand.
Mit Lazarus sehe ich grün
Mit Java und C/C++ sehe ich rot

Warf
Beiträge: 1908
Registriert: Di 23. Sep 2014, 17:46
OS, Lazarus, FPC: Win10 | Linux
CPU-Target: x86_64

Re: Array lockere Bereichs- (Range-) check

Beitrag von Warf »

Mathias hat geschrieben:Im Prinzip könnte man die auch selbst mit einer verketteten Liste machen. Aber dies ist mehr Aufwand.


Ja wobei ArrayListen (wie es TList ist) andere Vorteile haben, z.B. sind diese Cachebar, das heißt sukzessive zugriffe auf elemente die nah beieinander leiegen brauchen da viel weniger zeit als das auflösen der Verketteten liste. In Java verwendet die LinkedList daher verkette blöcke von arrays, das aber selbst zu bauen ist mir definitiv zu viel aufwand. Ein Nachteil der ArrayLists ist allerdings, wenn der Heap fragmentiert ist und die Liste sehr groß, kann es sein das sie nicht vergrößert werden kann, obwohl es eigentlich genug freien speicher gäb, weil es einfach keinen großen zusammenhängenden block mehr gibt sondern nur viele kleine.

Ich verwende aktuell am liebsten die generischen TFPGList und TFPGObjectList klassen. Die machen einem das leben echt einfach

Mathias
Beiträge: 6164
Registriert: Do 2. Jan 2014, 17:21
OS, Lazarus, FPC: Linux (die neusten Trunk)
CPU-Target: 64Bit
Wohnort: Schweiz

Re: Array lockere Bereichs- (Range-) check

Beitrag von Mathias »

Ein Nachteil der ArrayLists ist allerdings, wenn der Heap fragmentiert ist und die Liste sehr groß, kann es sein das sie nicht vergrößert werden kann, obwohl es eigentlich genug freien speicher gäb, weil es einfach keinen großen zusammenhängenden block mehr gibt sondern nur viele kleine.
Ich habe mal bei einer Dynamischen Array die Länge immer um 1 erhöht, so wie ich mich erinnern mag ging es sehr lange, bis ich an irgend ein Limit stoss.

Ich lasse gerade folgende Code laufen, das geht ja ewig, bis da ein Fehler kommt.
Jetzt bin ich schon auf 1,6GByte. Mein PC hat 16GByte RAM.

Code: Alles auswählen

 
type
  TMil = array[0..1000000] of byte;
var
  i: integer;
  a: array of TMil;
begin
  repeat
    SetLength(a, Length(a) + 1);
    WriteLn(Length(a));
  until 1 = 2;
end
Mit Lazarus sehe ich grün
Mit Java und C/C++ sehe ich rot

Warf
Beiträge: 1908
Registriert: Di 23. Sep 2014, 17:46
OS, Lazarus, FPC: Win10 | Linux
CPU-Target: x86_64

Re: Array lockere Bereichs- (Range-) check

Beitrag von Warf »

Mathias hat geschrieben:Ich habe mal bei einer Dynamischen Array die Länge immer um 1 erhöht, so wie ich mich erinnern mag ging es sehr lange, bis ich an irgend ein Limit stoss.

Ich lasse gerade folgende Code laufen, das geht ja ewig, bis da ein Fehler kommt.
Jetzt bin ich schon auf 1,6GByte. Mein PC hat 16GByte RAM.

Code: Alles auswählen

 
type
  TMil = array[0..1000000] of byte;
var
  i: integer;
  a: array of TMil;
begin
  repeat
    SetLength(a, Length(a) + 1);
    WriteLn(Length(a));
  until 1 = 2;
end

Wenn du linux verwendest kommen noch ein paar gb swap dazu, da kannst du ne ganze weile warten. Um an das fragmentierungsproblem zu stoßen müsstest du aber noch viel mehr dynamische Speicherzellen erstellen und dann zum teil wieder freigeben. Z.B. könntest du eine reihe an zeigern dir mit getmem besorgen (zufällige größe), dann zufällig welche davon wieder löschen, und dann das ganze mit einer TList versuchen (die wächst schneller). Wenn du das mit genug objekten machst solltest du recht schnell an die fragmentierungsprobleme stoßen (normalerweise treten die halt auf wenn eine software bereits schon lange läuft und sehr oft neue objekte erstellt und gelöscht hat)

Mathias
Beiträge: 6164
Registriert: Do 2. Jan 2014, 17:21
OS, Lazarus, FPC: Linux (die neusten Trunk)
CPU-Target: 64Bit
Wohnort: Schweiz

Re: Array lockere Bereichs- (Range-) check

Beitrag von Mathias »

Es ist gar nicht so einfach den Speicher voll zu kriegen, Swap wurde immer noch nicht angegriffen.. :mrgreen:
Einzig, die Schleife wird langsamer.

Code: Alles auswählen

top - 21:29:38 up  1:20,  1 user,  load average: 1.12, 1.18, 1.09
Tasks: 250 gesamt,   2 laufend, 175 schlafend,   0 gestoppt,   0 Zombie
%CPU(s):  5.7 be,  7.6 sy,  0.0 ni, 86.5 un,  0.2 wa,  0.0 hi,  0.0 si,  0.0 st
KiB Spch : 16120824 gesamt,  8813876 frei,  5337316 belegt,  1969632 Puff/Cache
KiB Swap: 16735228 gesamt, 16735228 frei,        0 belegt.  9982556 verfü Spch
Mit Lazarus sehe ich grün
Mit Java und C/C++ sehe ich rot

Benutzeravatar
af0815
Lazarusforum e. V.
Beiträge: 6199
Registriert: So 7. Jan 2007, 10:20
OS, Lazarus, FPC: FPC fixes Lazarus fixes per fpcupdeluxe (win,linux,raspi)
CPU-Target: 32Bit (64Bit)
Wohnort: Burgenland
Kontaktdaten:

Re: Array lockere Bereichs- (Range-) check

Beitrag von af0815 »

Ich verwende folgendes für Tests

Code: Alles auswählen

procedure TForm1.BuTestitClick(Sender: TObject);
const
  coTestsize = 10*1024*1024;
  coKB = 1024;
  coMB = coKB * 1024;
var
  list : TList;
  i,j,aktIDX : Integer;
begin
  list := TList.Create;
  //
  for i := 1 to 10000 do begin
    if i mod 5 = 0 then Memo1.Append('Used size: ' + IntToStr((i*coTestsize) div (coMB))+ ' MB');
    aktIDX := list.Add(getmem(coTestsize)); // List of PChar
    if list.Items[aktIDX] = pointer(0) then begin
      Memo1.Append('No memory at : ' + IntToStr(i));
      exit;
    end;
    for j := 0 to coTestsize-1 do begin
      PChar(list.Items[aktIDX])[j] := #$FA;
    end;
  end;
  list.Free;
end;

Bei win32 ist ohne PE Flag bei 1.7 GB Schluss, mit PE-Flag ca. 3.7 GB
Bei win64 ist bei mir bei ca. 90GB Schluss Win10/64/Pro. Bei 24GB Basisram. Dabei sieht man, das Windows scheinbar den Speicher beim swappen komprimiert, da der Zuwachs im Swap nur marginal ist.

Ev. mal für Linux testen :-) dann habe hat man Vergleichswerte für Linux auch.
Blöd kann man ruhig sein, nur zu Helfen muss man sich wissen (oder nachsehen in LazInfos/LazSnippets).

Antworten