Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Object-Pascal / Delphi-Language (https://www.delphipraxis.net/32-object-pascal-delphi-language/)
-   -   Delphi FOR-Schleifenvariable muss eine lokale Variable sein. Warum? (https://www.delphipraxis.net/7877-schleifenvariable-muss-eine-lokale-variable-sein-warum.html)

madbrain 23. Aug 2003 17:25


FOR-Schleifenvariable muss eine lokale Variable sein. Warum?
 
Moin, moin!

Ich habe relativ viele Prozeduren / Funktionen (in einer Unit) die einen Zähler brauchen. Um Variablen zu sparen, habe ich diese Variable global deklariert.
Delphi hält davon aber nicht so viel:


FOR-Schleifenvariable muss eine einfache lokale Variable sein

Diese Fehlermeldung wird angezeigt, wenn die Steuervariable einer for-Anweisung keine einfache Variable ist (sondern beispielsweise eine Komponente eines Datensatzes), und wenn sie nicht lokal zu der Prozedur ist, die die for-Anweisung enthält.

Aus Gründen der Abwärtskompatibilität ist es zulässig, eine globale Variable als Steuervariable zu verwenden – der Compiler gibt in diesem Fall eine Warnmeldung aus. Beachten Sie, dass mit Verwendung einer lokalen Variable außerdem ein leistungsfähigerer Programmcode erzeugt wird.


Die Idee, alles möglichst lokal zu deklarieren ist ja gut, aber in diesem Fall doch eher unpraktisch !?

Warum wird dadurch "leistungsfähigerer Programmcode" erzeugt? :?:

Thx

Madbrain

Hansa 23. Aug 2003 17:30

Re: FOR-Schleifenvariable muss eine lokale Variable sein. Wa
 
Du mußt ausweichen auf eine WHILE oder REPEAT Schleife. Im Prinzip dasselbe, es sei denn Borland hat das in letzter Zeit geändert. Sieht aber wohl nicht so aus.

madbrain 23. Aug 2003 17:47

Re: FOR-Schleifenvariable muss eine lokale Variable sein. Wa
 
hmmm
ok danke. werd ich mal probieren, aber..

Warum???

Eine Repeat/While Schleife, ist doch im Prinzip auch nur ein For-Schleife mit einer anders formulierten Abbruchbedingung?

Hansa 23. Aug 2003 17:54

Re: FOR-Schleifenvariable muss eine lokale Variable sein. Wa
 
Wenn Du
Delphi-Quellcode:
for i := 1 to 10000 do
schreibst wird das ganze 10000 mal durchlaufen, egal was in der Schleife passiert. Wahrscheinlich geht es um Compiler-Optimierung. Globale Variable belegen außerdem für die ganze Zeit des Programmlaufs Speicher, lokale eben nicht. und und und. Kümmere Dich einfach nicht darum und mache es eben anders. Um etwas mehr Denkarbeit wirst Du aber wohl nicht drumrum kommen. 8)

sakura 23. Aug 2003 19:53

Re: FOR-Schleifenvariable muss eine lokale Variable sein. Wa
 
:hi:
Zitat:

Zitat von madbrain
hmmm
ok danke. werd ich mal probieren, aber..

Warum???

Wie Hansa schon sagte, es handelt sich um eine Compiler-Optimierung. Intern wird I gar nicht für die Schleife benötigt, die Schleife wird durch das Register ECX gesteuert (dazu noch rückwärts). Wenn auf I zugegriffen wird, wird zusätzlich ein anderes Register herangezogen (i.A. EAX bzw EDX). Wenn I jetzt nicht lokal wäre, dann könnte ein zweiter Thread auf I zugreifen, um zu ermitteln wie der aktuelle Wert von I ist.

:arrow: Oops, I wird gar nicht genutzt, diese Abfrage würde fehlschlagen.

Um dieser Eventualität vorzubeugen muss I lokal sein. Aus gleichem Grund kann I auch innerhalb einer for-Schleife nicht manipuliert werden.

Die Antwort mag nicht befriedigend für Dein Problem sein, ist aber der Grund dazu. Ich würde Dir empfehlen for-Schleifen zu nutzen, die sind seit Delphi 3 oder 4 i.A. performanter als while- und repeat-Schleifen.

...:cat:...

Christian Seehase 23. Aug 2003 20:12

Re: FOR-Schleifenvariable muss eine lokale Variable sein. Wa
 
Moin sakura,

fällt Dir eine Konstellation ein, bei der die Schleifenvariable in ECX liegt?

Ich hab's gerade noch einmal ausprobiert:

Fall 1 Optimierung ist ausgeschaltet:
Verwende ich i innerhalb der Schleife um den Wert ausgeben zu lassen (ShowMessage(IntToStr(i))), wird i in EBX gespeichert, weise ich i einer anderen Variablen zu, dann in EAX.
Wenn i innerhalb der Schleife nicht verwendet wird, dann liegt der Wert nur auf dem Stack und wird dort geändert und verglichen.
Interessanter Weise wird der Wert dann immer inkrementiert, und nicht dekrementiert.

Fall 2 Optimierung ist eingeschaltet:
i wird immer in EBX gespeichert. Wenn i nicht verwendet wird decrementiert, ansonsten inkrementiert.

sakura 23. Aug 2003 20:16

Re: FOR-Schleifenvariable muss eine lokale Variable sein. Wa
 
Hi Christian,

:oops: da ist was durch einander geraten. Stimmt, EBX ist Standard, da EAX und EDX öfter während Operationen anderweitig genutzt werden. Was habe ich in dem Moment nur gedacht :wall: Aber der Rest war okay, oder :-D

...:cat:...

Hansa 23. Aug 2003 20:26

Re: FOR-Schleifenvariable muss eine lokale Variable sein. Wa
 
@Madbrain: siehste, das da habe ich befürchtet. :mrgreen: Und noch ein kleiner, allerdings wichtiger Hinweis: Nimm Sakura nicht zu wörtlich, wenn er sagt Du solltest eine for Schleife benutzen. Wenn Du vorher irgendwas mit 100 Zahlen machen mußt und es sind wirklich genau 100, eventuell weniger, dann nimm FOR. Ansonsten geht es NICHT mit for, sondern nur mit while/repeat. Eine andere Unsitte ist, falls es sich z.B. um 5-stellige Zahlen handelt, von denen wieder nur 100 gebraucht werden, so was zu tun:
Delphi-Quellcode:
for i := 1 to 99999 do
Kommen dann noch Festplattenzugriffe hinzu, na dann gute Nacht. 8)

Christian Seehase 23. Aug 2003 20:30

Re: FOR-Schleifenvariable muss eine lokale Variable sein. Wa
 
Moin sakura,

das gemeine ist:
ECX zu nehmen wäre irgendwie logisch, da es hierfür ja einen Maschinenbefehl gäbe.

Mich hätte halt mal interessiert warum das nicht so ist. Ich vermute mal, dass darauf verzichtet wurde, weil sich dieser Befehl nur im verwenden liesse, wenn der Schleifenzähler in der Schleife nicht angesprochen wird.

JoelH 24. Aug 2003 08:03

hmm,
 
Zitat:

Zitat von madbrain
Die Idee, alles möglichst lokal zu deklarieren ist ja gut, aber in diesem Fall doch eher unpraktisch !?

Warum wird dadurch "leistungsfähigerer Programmcode" erzeugt? :?:

Thx

Madbrain

Es ist nicht unpraktisch, wieso auch ? Ist es so schwer immer die Zeile
Delphi-Quellcode:
 var i : integer;
zu tippen ?
zur not mach dir ein Shortcut.

Zum effizienteren Code. Ich denke globale Variablen werden in einem bestimmten Speicherbereich abgelegt der eventruell via FAR Calls etc. aufgerufen werden muss bzw. es durch ausvorkommen kann dass es so ist. Dadurch braucht der REchenr länger (auch wenn es nur einige Takte sind) um an die Werte zu kommen.

Daniel 24. Aug 2003 09:41

Re: FOR-Schleifenvariable muss eine lokale Variable sein. Wa
 
Könnte man globale Schleifenvariablen deklarieren, hätte man noch ein anderes Problem: Nämlich genau dann, wenn sich zwei dieser Schleifen überlagern und beide die Variablen für ihre Zwecke setzen und nutzen wollen. Diese Problematik kann natürlich ebenfalls bei WHILE-Schleifen auftreten.
Auch aus diesem Grunde Schleifen-Variablen grundsätzlich lokal deklarieren.

Luckie 24. Aug 2003 09:45

Re: FOR-Schleifenvariable muss eine lokale Variable sein. Wa
 
Und sehr gefährlich. Wer weiß von wo und wann eine gloabel Variable bei so einem großem Projekt verändert wird. Und sollte es diesbezüglich Probleme geben, dannwünsche ich dir "Happy debugging und wir sprechen uns in 5 Tagen noch mal." :p

JoelH 24. Aug 2003 09:45

hmm,
 
Hilfvariablen, was anderes sind es ja nicht, sollten immer lokal sein.Auch um seien COde austauschbar zu halten.

madbrain 24. Aug 2003 11:30

Re: FOR-Schleifenvariable muss eine lokale Variable sein. Wa
 
Vielen Dank für die zahlreichen Antworten!

Ich denke, ich hab die Compilerwarnung jetzt verstanden. :dancer2: :dancer:

Motzi 24. Aug 2003 12:01

Re: FOR-Schleifenvariable muss eine lokale Variable sein. Wa
 
@sakura: man kann über eine kleine "Pointer-Akrobatik" auch die for-Zählvariable verändern!
Delphi-Quellcode:
var
  i: Integer;

for i := 1 to 20 do
  Inc(Integer(Pointer(@i)^));
Mal schaun wie das dann intern abläuft...

Assarbad 24. Aug 2003 12:13

Re: FOR-Schleifenvariable muss eine lokale Variable sein. Wa
 
@Motzi, dann müßte das hier aber auch gehen ...

Delphi-Quellcode:
var
  i: Integer;
  Iam_I:Integer absolute i;

for i := 1 to 20 do
  Inc(Iam_I);
Das wirft aber die ganze Optimierung über den Haufen!

Assarbad 24. Aug 2003 12:17

Re: FOR-Schleifenvariable muss eine lokale Variable sein. Wa
 
Nope, grade getestet. Das kompiliert nicht mal.

Delphi-Quellcode:
procedure CountMe;
var
  i: Integer;
begin
  for i := 1 to 20 do
    Inc(PInteger(@i)^);
end;
... wie bei Motzi kompiliert aber. Schaus mir grad mal näher an-

Motzi 24. Aug 2003 13:05

Re: FOR-Schleifenvariable muss eine lokale Variable sein. Wa
 
Habs mir jetzt mal näher angeschaut..

folgender Delphi-Code (das begin-end der for-Schleife ist unnötig, aber ganz bewusst gesetzt):
Delphi-Quellcode:
var
  i: Integer;
begin
  for i := 1 to 20 do
  begin
    Inc(PInteger(@i)^);
  end;
end;
wird in folgenden ASM-Code übersetzt:
Code:
Unit1.pas.30: for i := 1 to 20 do
    mov [eax], $00000001
Unit1.pas.32: Inc(PInteger(@i)^);
    inc dword ptr [eax]
Unit1.pas.33: end; // <- das end der Schleife
    inc dword ptr [eax]
Unit1.pas.30: for i := 1 to 20 do
    cmp dword ptr [eax], $15
    jnz -$09
In Zeile 32 wird die Zählvariable manuell erhöht, während in Zeile 33 (daher der unnötige begin-end Block) die "echte" Inkrementierung der Schleifenvariable stattfindet - beide Zeilen erzeugen denselben ASM-Code.
eax enthält eine Referenz in den Stack wo die eigentliche Zählvariable liegt.

Tryer 24. Aug 2003 13:08

Re: FOR-Schleifenvariable muss eine lokale Variable sein. Wa
 
Was der Compiler daraus macht unterscheidet sich nicht von dem hier:
Delphi-Quellcode:
procedure CountMe2;
var
  i: Integer;
begin
  i := 1;
  repeat
    Inc(PInteger(@i)^);
    Inc(i);
  until i = 21;
end;
Hier hat man den Vorteil das man die Abbruchbedingung kontrollieren könnte, z.B. zu "until i > 20".
Die For-Schleife frage immer nur auf Gleichheit ab, wodurch
Delphi-Quellcode:
For i := 0 to 20 do
  Inc(PInteger(@i)^)
zu einer Endlosschleife führt.

MfG,
Tryer

Assarbad 24. Aug 2003 13:16

Re: FOR-Schleifenvariable muss eine lokale Variable sein. Wa
 
Wohl wahr ...

Andere Compiler (zB VC) nehmen übrigens tatsächlich ECX, ist ja schließlich auch das Counter-Register ;)
Also lag sakura nicht sooo falsch *g*

Christian Seehase 24. Aug 2003 14:15

Re: FOR-Schleifenvariable muss eine lokale Variable sein. Wa
 
Moin Assarbad,

Zitat:

Zitat von Assarbad
Andere Compiler (zB VC) nehmen übrigens tatsächlich ECX, ist ja schließlich auch das Counter-Register

Allerdings würde dies den Compiler ein klein wenig komplizieren, da in diesem Falle extra unterschieden werden müsste, ob denn nun der Schleifenzähler innerhalb der Schleife benutzt wird, oder nicht.
In ersterem Falle ginge das nur, wenn heruntergezählt wird, da Loop ja dekrementiert.
So gesehen ist die Borland Variante bei eingeschalteter Optimierung immer EBX zu nehmen nicht gar so verkehrt.

Motzi 24. Aug 2003 14:50

Re: FOR-Schleifenvariable muss eine lokale Variable sein. Wa
 
Zitat:

Zitat von Christian Seehase
So gesehen ist die Borland Variante bei eingeschalteter Optimierung immer EBX zu nehmen nicht gar so verkehrt.

Tun sie aber nicht.. hab mir mal angeschaut wie diese Schleife übersetzt wird:
Delphi-Quellcode:
for i := 1 to 20 do
begin
end;
und sie wird über eax gesteuert! (Optimierung ist eingeschaltet!)

jbg 24. Aug 2003 15:56

Re: FOR-Schleifenvariable muss eine lokale Variable sein. Wa
 
Zitat:

Zitat von Christian Seehase
In ersterem Falle ginge das nur, wenn heruntergezählt wird, da Loop ja dekrementiert.

Zudem ist ein
Code:
LOOP Schleifenbeginn
mittlerweile langsamer als ein
Code:
DEC ECX
JNZ Schleifenbeginn
Und hierbei ist es egal welches Register benutzt wird.

Der Borland-Compiler nimmt ein Register, das gerade frei ist oder er macht eines frei. Welches er dann nimmt, ist hängt also zum einen vom vorhergehenenden Code und zum anderen vom Schleifenkörper ab.

Christian Seehase 24. Aug 2003 17:14

Re: FOR-Schleifenvariable muss eine lokale Variable sein. Wa
 
Moin Motzi,

stimmt, das kann ich nachvollziehen.
Allerdings hatte ich auch nicht mit leeren Schleifen getestet.

@jbg:
Kannst Du mir bitte mal die Quelle nennen, bezüglich der Ausführungsgeschwindigkeiten?
Würde mich mal interessieren, und in den Intel P4 Manuals (24547011,24547111,24547211) habe ich darüber nichts gefunden.

jbg 24. Aug 2003 18:00

Re: FOR-Schleifenvariable muss eine lokale Variable sein. Wa
 
Code:
Befehle     Taktzyklen
-----------------------
DEC ECX     1
JNZ Beginn  3

LOOP Beginn 6
Die Taktzyklen beziehen sich auf einen i486 und stammen aus dem Referenzhandbuch von Borland zu Turbo Assembler.


Hier ein Geschwindigkeitstest:
Delphi-Quellcode:
function RDTSC: Int64;
asm
        RDTSC
end;

function Test_Loop(Count: Integer): Int64;
asm
        MOV    ECX, EAX
        RDTSC            // Startzeit ermitteln (Result=EDX:EAX)
@@Repeat:
        LOOP   @@Repeat
end;

function Test_DecJcc(Count: Integer): Int64;
asm
        MOV    ECX, EAX
        RDTSC            // Startzeit ermitteln (Result=EDX:EAX)
@@Repeat:
        DEC    ECX
        JNZ    @@Repeat
end;

procedure TForm1.Button1Click(Sender: TObject);
const
  Count = $5fffffff;
var
  t1, t2: Int64;
begin
  try
    SetPriorityClass(GetCurrentProcess, REALTIME_PRIORITY_CLASS);
    SetThreadPriority(GetCurrentThread, THREAD_PRIORITY_TIME_CRITICAL);

    t1 := Test_Loop(Count);
    t1 := RDTSC - t1;

    t2 := Test_DecJcc(Count);
    t2 := RDTSC - t2;

  finally
    SetPriorityClass(GetCurrentProcess, NORMAL_PRIORITY_CLASS);
    SetThreadPriority(GetCurrentThread, THREAD_PRIORITY_NORMAL);
  end;

  Memo1.Lines.Add('LOOP    : ' + IntToStr(t1));
  Memo1.Lines.Add('DEC/Jcc: ' + IntToStr(t2));
  Memo1.Lines.Add('');
end;
Auf P4 2GHz kommen im Schnitt folgende Werte zustande:
Code:
LOOP  : 538596152
DEC/Jcc: 403735408

Christian Seehase 24. Aug 2003 18:17

Re: FOR-Schleifenvariable muss eine lokale Variable sein. Wa
 
Mion jbg,

danke.
Ich dachte schon, Du hättest da vielleicht noch andere Quellen.

Das Buch von Borland habe ich auch.

War es nicht gerade der Loop Befehl, der bei AMD so schnell implementiert war, dass Win98 ins Trudeln kam und gepatcht werden musste?

So gesehen könnte der Geschwindigkeitsvorteil sogar von der CPU abhängen.

jbg 24. Aug 2003 18:24

Re: FOR-Schleifenvariable muss eine lokale Variable sein. Wa
 
Zitat:

Zitat von Christian Seehase
So gesehen könnte der Geschwindigkeitsvorteil sogar von der CPU abhängen.

Das hängt sie immer. Die Intel Chip Architekten haben den LOOP Befehl einfach vernachlässigt. Man kann es auch so sehen, dass Intel Prozesooren im Druchschnitt alle "höheren" (CISC) Befehle viel langsamer ausführen als mehrere/viele "niedrige" (RISC) Befehle.

Hier noch ein schnellere Variante der Test_DECJcc Funktion für den P4:
Code:
        MOV    ECX, EAX
        RDTSC            // Startzeit ermitteln (EDX:EAX)
@@Repeat:
        SUB    ECX, 1
        JNZ    @@Repeat
Ein "SUB ECX,1" ist auf einem P4 schneller als ein DEC ECX. Man staune, aber es ist so.


Alle Zeitangaben in WEZ +1. Es ist jetzt 11:44 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