Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Algorithmen, Datenstrukturen und Klassendesign (https://www.delphipraxis.net/78-algorithmen-datenstrukturen-und-klassendesign/)
-   -   Array SCHNELL auf Null setzen (https://www.delphipraxis.net/182371-array-schnell-auf-null-setzen.html)

bernhard_LA 20. Okt 2014 09:11

Array SCHNELL auf Null setzen
 
was ist denn die schnellste Methode in Delphi ein dynmische Array
Delphi-Quellcode:
Result = array of array of integer;
mit Nullen zu befüllen ?

himitsu 20. Okt 2014 09:14

AW: Array SCHNELL auf Null setzen
 
Das
Delphi-Quellcode:
array of integer
mit FillChar/ZeroMemory
und das
Delphi-Quellcode:
array of array
mußt du dabei einzeln durchlaufen.

Ich weiß jetzt nicht genau, ob der Speicher beim Initialisieren mit 0 gef+llt wird, wenn ja, und wenn "schnell" = kurzer Code, dann eventuell auch so
Delphi-Quellcode:
A := nil;
SeltLength(A, L1, L2);

Blup 20. Okt 2014 09:20

AW: Array SCHNELL auf Null setzen
 
Beim Initialisieren wird das Array nicht mit Nullen gefüllt.
Delphi-Quellcode:
for n : = 0 to High(Result) do
  if Length(Result[n]) > 0 then
    FillChar(Result[n][0], SizeOf(Result[n][0]) * Length(Result[n]), #0);
Wenn es wirklich auf Geschwindigkeit an kommt ist eine Struktur DynArray in DynArray aber nicht so optimal.

Uwe Raabe 20. Okt 2014 09:28

AW: Array SCHNELL auf Null setzen
 
Zitat:

Zitat von Blup (Beitrag 1276655)
Beim Initialisieren wird das Array nicht mit Nullen gefüllt.

Das ist schlichtweg falsch!

Delphi-Quellcode:
SetLength
ruft intern
Delphi-Quellcode:
DynArraySetLength
aus System.pas auf. Dort steht dann (in XE7 Zeile 33931-33933) folgender Code:

Delphi-Quellcode:
  // Set the new memory to all zero bits
  if newLength > oldLength then
    FillChar((PByte(p) + elSize * oldLength)^, elSize * (newLength - oldLength), 0);

PMM 20. Okt 2014 10:59

AW: Array SCHNELL auf Null setzen
 
Ist das 0-setzten eine dokumentierte Eigenschaft? Oder könnte das in zukünftigen Delphi-Versionen auch mal anders gelöst sein?

Uwe Raabe 20. Okt 2014 11:03

AW: Array SCHNELL auf Null setzen
 
Zitat:

Zitat von PMM (Beitrag 1276676)
Ist das 0-setzten eine dokumentierte Eigenschaft? Oder könnte das in zukünftigen Delphi-Versionen auch mal anders gelöst sein?

Dokumentiert? ja!

Zitat:

Zitat von http://docwiki.embarcadero.com/Libraries/XE7/de/System.SetLength
Bei einer dynamischen Array-Variable weist SetLength dem in S referenzierten Array die angegebene Länge zu. Die vorhandenen Elemente des Arrays werden beibehalten, und der neu zugewiesene Platz wird auf 0 oder nil gesetzt. Bei multidimensionalen dynamischen Arrays kann SetLength mehrere Parameter für die Länge übernehmen. Die Parameteranzahl wird lediglich durch die Zahl der Array-Dimensionen beschränkt. Jeder Parameter gibt die Anzahl der Elemente für eine bestimmte Dimension an.


Blup 20. Okt 2014 11:46

AW: Array SCHNELL auf Null setzen
 
Dann sprechen wir von unterschiedlichen Delphi-Versionen, meine Hilfe sagt dazu:

Bei einer langen String- oder einer dynamischen Array-Variable weist SetLength dem in S referenzierten String oder Array die angegebene Länge zu. Dabei bleiben die Zeichen im String oder die Elemente im Array erhalten, jedoch ist der Inhalt des neu zugewiesenen Speicherbereichs nicht definiert. Eine Ausnahme stellt das Vergrößern der Länge von dynamischen Arrays dar, in dem die Elemente Typen sind, die initialisiert werden müssen (Strings, Varianten, Variant-Arrays oder Records, die solche Typen enthalten). Wenn S ein dynamisches Typen-Array ist, das initialisiert werden muss, wird der neu zugewiesene Platz auf 0 oder nil gesetzt.

Das scheint aber nicht mehr zu stimmen, ob es aber garantiert werden kann, dass der Speicher in jedem Fall gelöscht wird?

himitsu 20. Okt 2014 12:16

AW: Array SCHNELL auf Null setzen
 
Zitat:

Delphi-Quellcode:
SetLength(Result, 10);
// Array wird mit nil initialsiert, da die Elemente vom Typ array sind.
Hier ist das immer so.
Automatisch verwaltete Typen werden immer initialisiert, denn sonst könnten sie nicht autonatisch verwaltet werden. (von den entsprechenden Funktionen: SetLength, New, Create usw.,)

Sowas Einfaches, wie z.B. Integer, Char, Record, statisches Array usw., wird per se nicht initialisiert, es sei denn es liegt in einem Bereich, welcher immer initialisiert wird. (z.B. globale Variablen oder als Felder in Klassen)

Wie das nun beim Integer-Array aussieht, hab ich grade einfach nicht im Kopf.
Vorallem ob das bei allen Delphi-Versionen und Plattformen auch gleich ist ... ansonsten kann man bei sich ja einfach mal im Code Nachsehen, oder das SetLength debuggen.

Uwe Raabe 20. Okt 2014 13:30

AW: Array SCHNELL auf Null setzen
 
Zitat:

Zitat von Blup (Beitrag 1276684)
Dann sprechen wir von unterschiedlichen Delphi-Versionen,

...

Das scheint aber nicht mehr zu stimmen, ob es aber garantiert werden kann, dass der Speicher in jedem Fall gelöscht wird?

Das Verhalten ist auch schon in Delphi 7 so (ältere kann ich gerade nicht überprüfen). Ich gehe jetzt mal einfach davon aus, daß es in den zwischen D7 und XE7 liegenden Versionen ebenso ist. Natürlich kann niemand garantieren, daß dieses Verhalten in zukünftigen Versionen wieder anders implementiert wird, aber die Wahrscheinlichkeit ist eher gering. Sollte es wirklich mal soweit sein, wird man dann wohl aktiv werden müssen. Bis dahin sind sämtliche 0-Initialisierungen dynamischer Arrays im eigenen Code überflüssig.

Um auf die Originalfrage zurückzukommen: Himitsu's Vorschlag mit dem erweiterten SetLength ist die schnellste Möglichkeit.

D-User 20. Okt 2014 16:39

AW: Array SCHNELL auf Null setzen
 
was vllt auch noch erwähnt werden sollte:
bei größeren Arrays könnte es sich lohnen, dies ggf. mittels move umzusetzen, die greift per assembler auf mov zu, könnte noch mal einen Speed-Schub geben.
Hat nat. zur Voraussetzung, dass man irgendwo vorher einen Speicherbereich 0en hat und wird wohl auch nur dann sinnvoll, wenn mehr als ein mal aufgerufen wird (weil der initiale Bereich ja auch gefüllt werden muss, oder man findet vllt trickreich schon einen existierenden ;-) ).
Denk ich jetzt mal so.

Dejan Vu 21. Okt 2014 04:13

AW: Array SCHNELL auf Null setzen
 
Also wie sieht denn nun der Code aus, um ein
Delphi-Quellcode:
array of array of integer
zu füllen?
Spontan:
Delphi-Quellcode:
Procedure ZeroDynamicMatrix (aMatrix : TMatrix; rows, cols : Integer);
var
  i : Integer;
  NumberOfBytesPerRow : Integer;

Begin
  setLength(aMatrix,rows);
  NumberOfBytesPerRow := cols*sizeof(aMatrix[0][0]); // sollte gehen, da compile time constant.
  for i:=0 to cols-1 do begin
    setLength(aMatrix[i], cols);
    FillChar(aMatrix[i][0], NumberOfBytesPerRow);
  end;
end;
Optimierungsvorschläge?

Namenloser 21. Okt 2014 05:06

AW: Array SCHNELL auf Null setzen
 
Was man noch machen kann, wenn das Array immer „rechteckig“ ist, ist, kein Array of Array zu nehmen, sondern den ganzen Speicherbereich am Stück anzufordern, und dann den Index von Hand (bzw. über entsprechende Getter und Setter) auszurechnen.

Statt:
Delphi-Quellcode:
var a: array of array of integer;

SetLength(a, rows);

for i := 0 to rows - 1 do
  SetLength(a[i], cols);
So:
Delphi-Quellcode:
var a: array of integer;

SetLength(a, rows*cols);
a[y,x] im ersten Beispiel entspricht dann a[y*cols + x] im zweiten Beispiel. Dadurch spart man erstens die Pointer-Dereferenzierungen auf der ersten Ebene, und außerdem ist der Zugriff auf die Elemente cacheeffizienter, weil es zwischendrin keine Lücken gibt. Beim naiven Ansatz könnte der Speichermanager die Unterarrays sonstwohin verstreuen.

Außerdem kann man das ganze Array dann mit einem einzigen Aufruf von FillChar nullen und braucht dazu keine äußere Schleife mehr.

samso 21. Okt 2014 06:47

AW: Array SCHNELL auf Null setzen
 
Nach meinem Kenntnisstand ist der Delphi-Compiler intelligent genug um gleichförmige Arrays zu erkennen.
Statt:
Code:
for i := 0 to rows - 1 do
   SetLength(a[i], cols);
formuliert man

Code:
SetLength(a, rows, cols);
Edit 1: Sorry, ich habe übersehen, dass das ja schon in #2 stand.
Edit 2: Leider ist der Compiler anscheinend doch zu doof. :-( ich bin frustriert. Vielleicht hätte ich bei Assembler bleiben sollen...

Stevie 21. Okt 2014 07:42

AW: Array SCHNELL auf Null setzen
 
Was soll daran doof sein? Ein gleichförmiges zweidimensionales Array ist numal kein eindimensionales Array, obwohl man es als solches implementieren kann.
Und dann auch nur so weit, wie ich sichergehen kann, dass man nicht noch nachträglich eine Dimension verändert (dynamisch und so...)

greenmile 21. Okt 2014 08:10

AW: Array SCHNELL auf Null setzen
 
Zitat:

Zitat von Uwe Raabe (Beitrag 1276656)
Zitat:

Zitat von Blup (Beitrag 1276655)
Beim Initialisieren wird das Array nicht mit Nullen gefüllt.

Das ist schlichtweg falsch!

Delphi-Quellcode:
SetLength
ruft intern
Delphi-Quellcode:
DynArraySetLength
aus System.pas auf. Dort steht dann (in XE7 Zeile 33931-33933) folgender Code:

Delphi-Quellcode:
  // Set the new memory to all zero bits
  if newLength > oldLength then
    FillChar((PByte(p) + elSize * oldLength)^, elSize * (newLength - oldLength), 0);

Cool, das ist gut zu wissen. So wie jede Variable initialisiere ich bisher jedes Array.

samso 21. Okt 2014 08:16

AW: Array SCHNELL auf Null setzen
 
@Stevie:
Vielleicht war meine Formulierung zu hart. Ich hätte auch sagen können: Leider ist der Compiler nicht so schlau ein gleichförmiges mehrdimensionales dynamisches Array anders zu behandeln als ein ungleichförmiges mehrdimensionales dynamisches Array. Ohne dies jemals zu prüfen, ging ich bisher davon aus, dass die Sequenz
Code:
a: array of array of Integer;
etwas anderes wäre als
Code:
a: array of TIntegerDynArray;
Ich habe jetzt gelernt: Sofern die Darstellung des Arrays im Speicher eine Rolle spielt, muss der Programmierer ein gleichförmiges mehrdimensionales dynamisches Array selbst verwalten (wie in #12 gezeigt).

Uwe Raabe 21. Okt 2014 08:59

AW: Array SCHNELL auf Null setzen
 
Zitat:

Zitat von Dejan Vu (Beitrag 1276767)
Also wie sieht denn nun der Code aus, um ein
Delphi-Quellcode:
array of array of integer
zu füllen?

Wie schon erwähnt, ruft SetLength intern DynArraySetLength auf. Diese Methode ist rekursiv und arbeitet auch mit mehrdimensionalen Arrays. Ein
Delphi-Quellcode:
arr: array of array of Integer
wird also mit einem Aufruf von
Delphi-Quellcode:
SetLength(arr, 10, 20)
ebenso mit Nullen initialisiert wie ein eindimensionales.

hoika 21. Okt 2014 09:00

AW: Array SCHNELL auf Null setzen
 
Hallo,

also ich verlasse mich nicht auf automatisches Setzen des Compilers.
Da wir Datenbankprogramme entwickeln,
spielt der Overhead des Initialisierens hier überhaupt keine Rolle.

Heiko

Uwe Raabe 21. Okt 2014 09:04

AW: Array SCHNELL auf Null setzen
 
Zitat:

Zitat von hoika (Beitrag 1276777)
also ich verlasse mich nicht auf automatisches Setzen des Compilers.

Ist ja nicht direkt der Compiler, sondern die entsprechende Prozedur der RTL. Aber wie du meinst.

p80286 21. Okt 2014 10:27

AW: Array SCHNELL auf Null setzen
 
Zitat:

Zitat von hoika (Beitrag 1276777)
Hallo,

also ich verlasse mich nicht auf automatisches Setzen des Compilers.

Da kann ich nur zustimmen, auch wenn ich das gleiche wie ... mache, dann steht's in meinen Sourcen, in 5 Jahren weiß doch keiner mehr ob XE2 dreidimensionale arrays mit 0 initialisiert hat oder ob nicht.

Gruß
K-H

Stevie 21. Okt 2014 10:52

AW: Array SCHNELL auf Null setzen
 
Zitat:

Zitat von p80286 (Beitrag 1276793)
Zitat:

Zitat von hoika (Beitrag 1276777)
Hallo,

also ich verlasse mich nicht auf automatisches Setzen des Compilers.

Da kann ich nur zustimmen, auch wenn ich das gleiche wie ... mache, dann steht's in meinen Sourcen, in 5 Jahren weiß doch keiner mehr ob XE2 dreidimensionale arrays mit 0 initialisiert hat oder ob nicht.

Dazu sag ich nur cargo cult programming.

Das sind so dermaßen grundlegende Dinge in der RTL, die nicht einfach in irgendeiner Version verändert werden.

Uwe Raabe 21. Okt 2014 11:04

AW: Array SCHNELL auf Null setzen
 
Zitat:

Zitat von Stevie (Beitrag 1276799)
Dazu sag ich nur cargo cult programming.

Genau das hatte ich auch schon auf der Zunge :-D

p80286 21. Okt 2014 11:10

AW: Array SCHNELL auf Null setzen
 
Zitat:

Zitat von Stevie (Beitrag 1276799)
Zitat:

Zitat von p80286 (Beitrag 1276793)
Zitat:

Zitat von hoika (Beitrag 1276777)
Hallo,

also ich verlasse mich nicht auf automatisches Setzen des Compilers.

Da kann ich nur zustimmen, auch wenn ich das gleiche wie ... mache, dann steht's in meinen Sourcen, in 5 Jahren weiß doch keiner mehr ob XE2 dreidimensionale arrays mit 0 initialisiert hat oder ob nicht.

Dazu sag ich nur cargo cult programming.

Das sind so dermaßen grundlegende Dinge in der RTL, die nicht einfach in irgendeiner Version verändert werden.

Dann ist Dein Code aber zunächst nur EmbaDelphi-tauglich. Bevor Du Dich nicht eingearbeitet hast kannst Du Dir nicht sicher sein, daß die 1001 anderen Delphi/Pascal-Entwicklungsumgebungen genauso arbeiten. Ein fillchar() zeigt dem Sourcecodeleser übrigens eindeutig, das da etwas zu initialisieren ist, fehlt dies muß er sich erst einmal schlau machen.

Gruß
K-H

Stevie 21. Okt 2014 11:35

AW: Array SCHNELL auf Null setzen
 
Zitat:

Zitat von p80286 (Beitrag 1276808)
Dann ist Dein Code aber zunächst nur EmbaDelphi-tauglich. Bevor Du Dich nicht eingearbeitet hast kannst Du Dir nicht sicher sein, daß die 1001 anderen Delphi/Pascal-Entwicklungsumgebungen genauso arbeiten. Ein fillchar() zeigt dem Sourcecodeleser übrigens eindeutig, das da etwas zu initialisieren ist, fehlt dies muß er sich erst einmal schlau machen.H

Joa, is ja auch die Delphi-Praxis hier und nicht die "1001 Pascal-Praxis".
Wenn du mir die eine andere Pascal Implementierung zeigst, die generics, closures und enhanced rtti kann,
dann können wir nochmal über meinen derzeit nur Embarcadero kompatiblen Code sprechen :)

Dejan Vu 21. Okt 2014 12:20

AW: Array SCHNELL auf Null setzen
 
Also 'cargo cult programming' ist das ja nun nicht, wenn ich eine Variable explizit initialisiere, d.h. auf 0 setze. Denn manchmal meckert der Compiler und manchmal nicht, manchmal muss man initialisieren, manchmal nicht. Das ist doch inkontinent inkonsistent.

Wenn ich ein Array auf '1' initialisiere, muss ich Initialisierungscode schreiben, bei Initialisierung auf '0' nicht? Was soll das?

Man *kann* auf das Nullen verzichten, denn die RTL oder auch Windows macht das schon für einen, aber aus Dokumentationsgründen sollte man (finde ich) Werte explizit initialisieren. Dann fällt man auch nicht in den Matsch, wie Du in deinem Blog(*) mal erwähnt hast. Ich weiß jetzt nicht, ob das passt, aber imho wäre das im verlinkten Artikel beschriebene Problem mit den Packages bei expliziter Initialisierung (im initialization-Abschnitt) kein Problem mehr (aber ich kann mich hier wirklich irren).

Ich persönlich neige dazu, die Initialisierung grundsätzlich vorzunehmen. Ausgenommen, ich benötige jede Nanosekunde an Performance.

(*) Ein *SEHR* empfehlenswerter Blog, liebe Mitleser!

mkinzler 21. Okt 2014 12:30

AW: Array SCHNELL auf Null setzen
 
Wenn man schon bei so rudimentären Dingen der RTL nicht vertraut, sollte man besser alles selber schreiben[Zynikmodus] auch den Compiler und das Betriebssystem, denn man weiss ja nicht, welchen Mist die verantwortlichen Entwickler verzapft haben[/Zynikmodus]

Stevie 21. Okt 2014 13:07

AW: Array SCHNELL auf Null setzen
 
Zitat:

Zitat von Dejan Vu (Beitrag 1276818)
Man *kann* auf das Nullen verzichten, denn die RTL oder auch Windows macht das schon für einen, aber aus Dokumentationsgründen sollte man (finde ich) Werte explizit initialisieren. Dann fällt man auch nicht in den Matsch, wie Du in deinem Blog(*) mal erwähnt hast. Ich weiß jetzt nicht, ob das passt, aber imho wäre das im verlinkten Artikel beschriebene Problem mit den Packages bei expliziter Initialisierung (im initialization-Abschnitt) kein Problem mehr (aber ich kann mich hier wirklich irren).

Das ist eine hässliche Situation, da stimme ich zu. Allerdings spielen dort andere Umstände eine Rolle, nämlich, dass der finalization Block nicht zur Lazy initialization passt. Die prüft nämlich auf assigned, der finalization Code setzt aber nicht auf nil. Da es sich hier um ein Package handelt, wird die "globale Variablen werden genullt Prozedur" nur einmal (nämlich beim Laden des Packages ausgeführt). Initialization/Finalization in diesem Fall bei jedem Laden der benutzenden DLL.

Zitat:

Zitat von Dejan Vu (Beitrag 1276818)
(*) Ein *SEHR* empfehlenswerter Blog, liebe Mitleser!

Schleimer! - nee Spaß, danke für die Blumen. :)

himitsu 21. Okt 2014 13:09

AW: Array SCHNELL auf Null setzen
 
Zitat:

Leider ist der Compiler nicht so schlau ein gleichförmiges
Das hat nichts mit "schlau" zu tun.
Es ist einfach nicht möglich alle SubArrays in eine Reihe zu legen (OK, möglich teilweise schon, aber sinnlos), um dann alles mit einem FillChar füllen zu können.

Das fällt sofort auf, wenn man weiß wie so ein dynamisches Array aufgebaut ist und daß die Verwaltungsdaten (Länge und Referenzzählung) auch noch vor dem Ersten (Nullten) Feld liegen.

Dejan Vu 21. Okt 2014 13:20

AW: Array SCHNELL auf Null setzen
 
Zitat:

Zitat von Stevie (Beitrag 1276829)
Zitat:

Zitat von Dejan Vu (Beitrag 1276818)
(*) Ein *SEHR* empfehlenswerter Blog, liebe Mitleser!

Schleimer! - nee Spaß, danke für die Blumen. :)

Zuckerbrot und Peitsche :mrgreen:

Im Ernst.

Immer Initialisieren hilft. und PAttern wie:
Delphi-Quellcode:
initialization
  InitializeGlobalStuff();
finalization
  FinalizeGlobalStuff();

und
Constructor Create();
begin
  InitializeFields();
End;

Destructor Destroy();
begin
  FinalizeFields();
End;
Sind einfacher und sicherer, wenn sie so stringent durchgezogen werden, das selbst die Reihenfolge der Finalisierungen umgekehrt zu den Initialisierungen ist (sofern sinnvoll). Einfach oberspießige Ordnung. Normalerweise nicht mein Ding, aber hier schon.

Und meine Initialisierungen IMMER explizit (dafür vielleicht redundant). WTF.


Alle Zeitangaben in WEZ +1. Es ist jetzt 03:19 Uhr.

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