Autor Beitrag
Mathematiker
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
Beiträge: 2622
Erhaltene Danke: 1447

Win 7, 8.1, 10
Delphi 5, 7, 10.1
BeitragVerfasst: So 10.05.15 10:32 
Hallo,
bei meinem Versuch ein Programm zu "optimieren" bin ich wieder auf ein Problem gestoßen, dass ich nicht verstehe. Der Sonntag ist auf Anweisung meiner Frau :wink: zwar computerfrei, aber es lässt mir keine Ruhe und sie ist gerade beschäftigt. :mrgreen:

Konkret: Bisher habe ich einige globale Variablen benutzt (ich weiß, die sind "böse") und will sie in den private-Abschnitt der jeweiligen Klasse verschieben. Funktioniert problemlos, warum auch nicht.
Genau verstanden, warum dies programmtechnisch besser ist, habe ich noch nicht. Das ist aber auch nicht mein Problem.

Das Problem ist: Verschiebe ich z.B. nur eine Variable
ausblenden Delphi-Quelltext
1:
2:
implementation
 var feld : integer;
nach
ausblenden Delphi-Quelltext
1:
2:
private
 feld : integer;
steigt die Größe der Unit (Debuginformationen abgeschaltet) um 80 Byte und die Exe selbst wird ein halbes KByte größer.
Ähnliches geschieht auch bei anderen globalen Variablen. Mitunter reduziert sich die Größe aber erheblich, mitunter steigt sie extrem an.
Insbesondere bei Feldern wie z.B.
ausblenden Delphi-Quelltext
1:
  belegung : array[0..15,0..15of integer;					
wird in einer konkreten Situation die Unit 200 Byte kleiner, die Exe aber 1 KB größer.
Dies sind alles keine dramatischen Veränderungen; allerdings in der Summe kann einiges zusammenkommen.
Ich verstehe einfach das Prinzip bzw. die dahinter steckende Logik nicht.
Ganz verrückt ist, dass ich manchmal, aber selten, beim Verschieben in den private-Teil auch den ominösen internen Fehler BC2241 bekomme.
Weiß jemand von Euch, was hier vorgeht?

Danke
Mathematiker

Nachtrag: Es geht noch heftiger. Bei der Struktur
ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
type
  teinzel = record ecke : array[1..8of record x,y,z:double end;
                   mx,my,mz : double;
                   szahl : integer;
                   seite : array[1..6of integer;
                   mi : array[1..6of record x,y,z:double end;
             end;
  twuerfel = array[1..4,1..4,1..4of teinzel;
und 3 Variablen vom Typ twuerfel sind es im private-Teil gleich 10KB mehr.

_________________
Töten im Krieg ist nach meiner Auffassung um nichts besser als gewöhnlicher Mord. Albert Einstein
Delphi-Laie
ontopic starontopic starontopic starontopic starontopic starontopic starofftopic starofftopic star
Beiträge: 1600
Erhaltene Danke: 232


Delphi 2 - RAD-Studio 10.1 Berlin
BeitragVerfasst: So 10.05.15 12:12 
user profile iconMathematiker hat folgendes geschrieben Zum zitierten Posting springen:
Der Sonntag ist auf Anweisung meiner Frau :wink: zwar computerfrei, aber es lässt mir keine Ruhe und sie ist gerade beschäftigt. :mrgreen:


Die Katze läßt also das Mausen nicht. Nun, Mathematiker, in einem solchen Forum wie diesem wirst Du für diese Deine bzw. unser aller Sucht ganz bestimmt auf sehr viel Verständnis stoßen. ;-)

user profile iconMathematiker hat folgendes geschrieben Zum zitierten Posting springen:
Genau verstanden, warum dies programmtechnisch besser ist, habe ich noch nicht.


Die Fehleranfälligkeit ist bei lokalen Variablen geringer, weil ihre Sichtbarkeit mit "zunehmender Lokalisierung" verringert wird. Man verschiebt sie deshalb möglichst weit nach "innen". Das weißt Du doch ganz bestimmt auch.

Zur Größe des Compilates: Das wird nach meiner Beobachtung immer in ziemlich großen Sprüngen erhöht, jedenfalls nicht linear zur Menge des benötigten Speichers.


Zuletzt bearbeitet von Delphi-Laie am So 10.05.15 14:58, insgesamt 5-mal bearbeitet

Für diesen Beitrag haben gedankt: Mathematiker
jaenicke
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 19272
Erhaltene Danke: 1740

W11 x64 (Chrome, Edge)
Delphi 11 Pro, Oxygene, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
BeitragVerfasst: So 10.05.15 14:18 
Wenn eine Variable nur im privaten Teil einer Klasse vorkommt, kann man auch nur innerhalb der Klasse darauf zugreifen. Auch bei Veröffentlichung nach außen durch eine Property kommt man nur über die Property heran. Man weiß also, dass nicht irgendwo im Code noch ein separater Zugriff existiert, der doch noch etwas anderes hineinschreibt und kann über Getter und Setter jeden Zugriff von außen kontrollieren (notfalls im Debugger).

Die Größe der Exe ändert sich unter anderem durch den Code, der zum Zugriff erzeugt wird. Wenn man z.B. von außen auf eine Property mit Index zugreift, muss die Berechnung der Position in einem Array nur einmal im Getter und Setter im Code stehen, während von außen nur noch Funktionsaufrufe passieren. Das ist nur ein Beispiel, konkret kann man das nur im Assemblercode anschauen was im konkreten Fall passiert.

Für diesen Beitrag haben gedankt: Mathematiker
Popov
ontopic starontopic starontopic starontopic starhalf ontopic starofftopic starofftopic starofftopic star
Beiträge: 1655
Erhaltene Danke: 13

WinXP Prof.
Bei Kleinigkeiten D3Pro, bei größeren Sachen D6Pro oder D7
BeitragVerfasst: So 10.05.15 14:46 
Bevor wir weiter darüber reden, zuerst einer Klärung: reden wir über (Textdatei) Pas-Unit oder (Binärdatei) Dcu-Unit?

Zuerst allgemein zu Datenträgern. Sie bestehen aus verschiedenen Bereichen, eines davon ist der Sektor bzw. Datenblock. Ein Datenblock ist die kleinste Einheit auf dem Datenträger, noch kleiner geht es nicht. Wenn also Informationen auf den Datenträger gespeichert werden, dann wird mindestens ein Datenblock verbraucht, auch wenn die eigentliche Information kleiner ist. Selbst wenn es nur 1 Byte ist, es wird in dem Fall 1 Datenblock verbracht.

Das kann man auch selber testen. Auf der Festplatte über den Datenexplorer eine leere Txt-Textdatei erstellen. Die Dateieigenschaften sagen uns, dass die Datei 0 Byte groß ist. Die Datei ist ja noch leer. Nun schreiben wir ein Ascii-Zeichen rein, z. B. ein A, das macht 1 Byte. Die Größe der Datei ist jetzt 1 Byte, auf dem Datenträger ist der Verbrauch aber 4 KiB.

Wie gesagt, du warst etwas ungenau mit der Beschreibung der Unit. Welche Unit, wo steigt der Verbrauch? Evtl. meinst du was anderes, aber das auf die Schnelle als Grundinformation.

Was die Exe angeht, nun, der Delphi-Compiler ist schon gut, d. h. er optimiert und wirft vieles raus was der Nutzer für wichtig hält, aber letztendlich unwichtig ist. Trotzdem, man sollte stets in Hinterkopf behalten, dass man hier nicht das Programm selbst in Assembler programmiert (damit meine ich nicht kleine Codestückchen, sondern ganzes Programm), wo drei weitere Bytes nur drei weitere Bytes bedeuten, sondern es einem Compiler überlässt den Code zu deuten. Und wenn der Compiler meint, dass man für die Verarbeitung der drei Bytes eine Routine aus der Bibliothek braucht die 10 KiB groß ist, dann erhöht sich alles eben um 10 KiB.

Zitat:
Genau verstanden, warum dies programmtechnisch besser ist, habe ich noch nicht.


Das ist wieder so ein Thema für eine Grundsatzdiskussion :wink:

Delphi-Laie hat es schon angedeutet, man kann damit gelegentlich so kräftig auf die Schnauze fallen. Beispiel:

ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
var
  i: Integer;

procedure TForm1.Button1Click(Sender: TObject);
begin
  for i := 0 to 5 do
    ShowMessage('Schleife ' + IntToStr(i));
end;

procedure TForm1.Button2Click(Sender: TObject);
begin
  ShowMessage('i wurde in der anderen Prozedur gezählt bis: ' + IntToStr(i - 1));
end;


Obwohl in der einen Prozedur i für eine Schleife genutzt wird, kann man in der zweiten Prozedur den Wert von i anrufen.

Kann praktisch sein, aber:

ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
var
  i: Integer;

procedure TForm1.Button1Click(Sender: TObject);
begin
  for i := 0 to 5 do
    ShowMessage('Schleife ' + IntToStr(i));
end;

procedure TForm1.Button2Click(Sender: TObject);
var
  i: Integer;
begin
  ShowMessage('i wurde in der anderen Prozedur gezählt bis: ' + IntToStr(i - 1));
end;


Hier stimmt das Ergebnis plötzlich nicht mehr. Warum? Weil hier i in der zweiten Prozedur eine lokale Variable ist. Delphi gibt der lokalen Variable bei gleichem Namen immer den Vorzug.

Bei so einem Miniprogramm ist das egal, man sieht ob i hier global oder lokal ist. Was ist aber mit einem großen Projekt der über mehrere Units geht und wo man eine globale Variable Width für die Breite eines Werkstücks nutzt? Und dann kommt ein Kollege und braucht Width für die Breite eines Smilies. Eigentlich wollte er Width lokal nutzen, hat aber vergessen es lokal einzutragen. Natürlich gab es keine Fehlermeldung, denn der Compiler fand eine Width Variable (die globale). Und plötzlich wundern sich alle, dass aus irgendeinem Grund das Werkstück länger wird, je breiter das Smili lacht.

_________________
Popov

Für diesen Beitrag haben gedankt: Mathematiker
Delphi-Laie
ontopic starontopic starontopic starontopic starontopic starontopic starofftopic starofftopic star
Beiträge: 1600
Erhaltene Danke: 232


Delphi 2 - RAD-Studio 10.1 Berlin
BeitragVerfasst: So 10.05.15 14:52 
Noch etwas zu den Compilatsgrößen: Die DCUs werden auch umso größer, je mehr der Quelltext auf möglichst viele Zeilen "gestreckt" wird, und umso kleiner, je mehr der Quelltext komprimiert wird, also möglichst viele Anweisungen in jeder Zeile stehen. Das ist insofern plausibel, weil im ersteren Falle mehr Breakpoints gesetzt werden (müssen). Was mich nur wundert: Daß die Größe der Exe-Dateien von diesen DCU-Größen nicht abzuhängen scheint. Ob die Breakpoints beim Linken wieder entfernt werden - und das bei der atemberaubenden Geschwindigkeit des Compilers (inkl. Linkers)?


Zuletzt bearbeitet von Delphi-Laie am So 10.05.15 14:54, insgesamt 1-mal bearbeitet

Für diesen Beitrag haben gedankt: Mathematiker
WasWeißDennIch
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 653
Erhaltene Danke: 160



BeitragVerfasst: So 10.05.15 14:53 
Da im Ausgangspost etwas von "in den private-Abschnitt verschoben" steht, kann es hier gar nicht um globale vs. lokale Variablen gehen, sondern wohl eher um globale Variablen vs. private Felder.

Für diesen Beitrag haben gedankt: Mathematiker
Mathematiker Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
Beiträge: 2622
Erhaltene Danke: 1447

Win 7, 8.1, 10
Delphi 5, 7, 10.1
BeitragVerfasst: So 10.05.15 16:03 
Hallo,
Danke an alle für die Hinweise und Erklärungen.
Prinzipiell ist mir schon klar, weshalb es vernünftig ist, keine globalen Variablen zu nutzen und, wenn möglich, solche Konstrukte in die Klasse einzubinden. Deshalb umgehe ich globale Variablen ja auch weitestgehend.

Ich war vor allem verwundert, dass die kompilierte DCU (also nicht die PAS-Datei) teilweise so stark anwächst.
Das Argument, das Variablen/Felder(?) im private-Bereich von außen nicht mehr sichtbar und nicht ohne Weiteres verändert sind, ist einleuchtend.

Dennoch möchte ich "ketzerisch" noch einmal fragen:
Liegen die Variablen global im implementation-Abschnitt (nicht nach interface!), so können sie doch von außen gar nicht "gesehen" werden. Enthält die Unit nur genau eine Klasse und ausschließlich deren Methoden, so sind die Variablen doch auch sicher und ungewollte Reaktionen unmöglich.
Ich werde wohl darüber weiter nachdenken müssen.

Danke nochmals
Mathematiker

_________________
Töten im Krieg ist nach meiner Auffassung um nichts besser als gewöhnlicher Mord. Albert Einstein
Delphi-Laie
ontopic starontopic starontopic starontopic starontopic starontopic starofftopic starofftopic star
Beiträge: 1600
Erhaltene Danke: 232


Delphi 2 - RAD-Studio 10.1 Berlin
BeitragVerfasst: So 10.05.15 16:20 
user profile iconMathematiker hat folgendes geschrieben Zum zitierten Posting springen:
Dennoch möchte ich "ketzerisch" noch einmal fragen:


Eine Frage kam genaugenommen nicht, und ketzerisch war daran auch nichts.

user profile iconMathematiker hat folgendes geschrieben Zum zitierten Posting springen:
Liegen die Variablen global im implementation-Abschnitt (nicht nach interface!), so können sie doch von außen gar nicht "gesehen" werden. Enthält die Unit nur genau eine Klasse und ausschließlich deren Methoden, so sind die Variablen doch auch sicher und ungewollte Reaktionen unmöglich.
Ich werde wohl darüber weiter nachdenken müssen.


Darüber eher nicht, denn das dürfte korrekt sein.

Vielleicht meldet sich Sebastian Jänicke hier auch noch mal zu Wort, denn der weiß bezüglich der Delphiprogrammierung eigentlich alles und deshalb auch zu jedem Thema eine richtige Antwort zu geben....auch das meine ich nicht ironisch.


Zuletzt bearbeitet von Delphi-Laie am So 10.05.15 16:36, insgesamt 1-mal bearbeitet
WasWeißDennIch
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 653
Erhaltene Danke: 160



BeitragVerfasst: So 10.05.15 16:27 
Unter "älteren" Delphi-Versionen ist diese globale implementation-Variable sogar die einzige Möglichkeit, so etwas wie Klassenvariablen zu simulieren. Ab Delphi 2006 (wenn ich mich recht entsinne) sind allerdings ein paar diesbezügliche Neuerungen hinzugekommen, nämlich Klassenvariablen (class var) und das Wörtchen strict. Klassenvariablen sind (wie der Name schon vermuten lässt) an eine Klasse gebunden und nicht an Instanzen dieser Klasse. Das bedeutet, dass man wenn man so will "klassenglobale" Variablen deklarieren kann. Deklariert man diese als strict private, so haben andere Klassen keinen Zugriff darauf, auch dann nicht, wenn sie in derselben Unit stehen.
Popov
ontopic starontopic starontopic starontopic starhalf ontopic starofftopic starofftopic starofftopic star
Beiträge: 1655
Erhaltene Danke: 13

WinXP Prof.
Bei Kleinigkeiten D3Pro, bei größeren Sachen D6Pro oder D7
BeitragVerfasst: So 10.05.15 16:41 
Na ja, ich weiß auch nicht wie man die eine globale Variable nennt. Eine lokale Variable nenne ich lokale Variable, eine globale Variable (im Interface-Teil) nenne ich globale Variable. Wie man die globale Variable im Implementation-Teil nennt, habe ich nie nachgedacht. Sie ist Unit-Global, auf der anderen Seite auch Programm-Lokal. Letztendlich ist sie aber global.

Dann gibt es noch die gefakte global Variable, die man der Welt als nicht global verkaufen will, sondern als Feldvariable. Statt sie global zu setzten, packt man sie in das private-Teil des Formulars, knallt noch ein F davor und sagt: ätschibätsch, die ist ja nicht global:

ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
type
  TForm1 = class(TForm)
  private
    { Private-Deklarationen }
    FMeineVariable: Integer; 
  public
    { Public-Deklarationen }
  end;


Technisch gesehen nicht. Praktisch gesehen stellt sich die Frage inwieweit sie ein Teil der Klasse ist, wenn ich sie global (innerhalb der Unit) nutze? Aber klar, genaugenommen ist sie dann nicht mehr global.

Letztendlich ist das Problem klar - es gibt die prozeduale und die objektorientierte Programmierung. Es widerspricht aber der Logik wieso ein Objekt, nachdem es freigegeben worden ist, immer noch Werte liefert (weil es mit einer globalen Variable gearbeitet hat)? Dann gibt es die prozeduale Programmierung. Auch hier sagt die Logik, es ist ist nur eine Prozedur, also eine in sich geschlossene Einheit. Eine Blackbox. Werte werden per Parameter übergeben, die Prozedur macht ihren festdefinierten Job, fertig. Prozeduren müssen jederzeit austauschbar sein, soweit die Parameter und das Ergebnis gleich sind.

Auf der anderen Seite, gelegentlich ist der Aufwand eine Variable lokal zu halten sehr arbeitsintensiv.

_________________
Popov
jaenicke
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 19272
Erhaltene Danke: 1740

W11 x64 (Chrome, Edge)
Delphi 11 Pro, Oxygene, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
BeitragVerfasst: So 10.05.15 17:23 
user profile iconMathematiker hat folgendes geschrieben Zum zitierten Posting springen:
Liegen die Variablen global im implementation-Abschnitt (nicht nach interface!), so können sie doch von außen gar nicht "gesehen" werden. Enthält die Unit nur genau eine Klasse und ausschließlich deren Methoden, so sind die Variablen doch auch sicher und ungewollte Reaktionen unmöglich.
Das ist natürlich richtig. Und in Delphi selbst gibt es auch solche Variablen, z.B. ClipboardFormats, FileFormats usw., in dem Fall in der Unit Graphics.

Dort werden die Variablen über Zwischenfunktionen jedoch auch in anderen Units bestückt, nur auf die Listen selbst wird nicht zugegriffen.

Wenn hingegen die Listen nur in einer Klasse isoliert benutzt würden, wäre es sinnvoller diese dort unter private bzw. als Klassenvariable zu deklarieren um den Scope möglichst gering zu halten. Je kleiner der Scope ist, desto weniger potentielle Auswirkungen hat eine Änderung der Implementierung z.B. der Klassen hinter diesen Variablen. Denn wenn diese nach außen nur über Funktionen erreichbar sind, reicht es z.B. diese zu testen um die Funktionalität nach außen sicherzustellen.

Für diesen Beitrag haben gedankt: Mathematiker
Mathematiker Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
Beiträge: 2622
Erhaltene Danke: 1447

Win 7, 8.1, 10
Delphi 5, 7, 10.1
BeitragVerfasst: So 10.05.15 21:41 
Hallo,
nochmals Danke für die Hinweise.
Wenn ich es richtig verstehe, ist es besser, gleichgültig ob die Exe "wächst", die globalen Variablen wenigstens als private zu "verstecken".
Genau das werde ich, soweit möglich, auch machen.

Beste Grüße
Mathematiker

_________________
Töten im Krieg ist nach meiner Auffassung um nichts besser als gewöhnlicher Mord. Albert Einstein