Autor Thema: gcc, GEMDOS Super und Stackzerstörung  (Gelesen 73943 mal)

0 Mitglieder und 1 Gast betrachten dieses Thema.

Offline czietz

  • Benutzer
  • Beiträge: 3.686
gcc, GEMDOS Super und Stackzerstörung
« am: Sa 06.08.2016, 14:15:46 »
Hallo,

ich habe aus Neugier mal meinen RAM-Tester YAART [1] von Pure C auf gcc und libcmini portiert, was mit sehr wenigen Änderungen möglich war. Ein hinterhältiger Bug hat mich aber doch Zeit gekostet. Daher beschreibe ich ihn hier kurz, in der Hoffnung anderen damit zu helfen.

Ich benutze die GEMDOS-Funktion Super, um innerhalb des Codes in den Supervisor-Modus zu schalten und später wieder zurück:

/* switch to supervisor mode so we can manipulate memory */
suret = Super(0);

/* Dinge, die nur im Supervisor-Modus möglich sind */
/* [...] */

/* back to user mode */
Super((void *)suret);

Nun hat der gcc-Optimierer die -- an sich nette -- Eigenschaft, den Stack nicht nach jedem Funktionsaufruf zu korrigieren, sondern die Korrekturen zu sammeln und an späterer Stelle en bloc durchzuführen. Dummerweise stellt aber bereits Super((void *)suret); den Stack zurück. gcc hat nun in meinem Fall die Stackkorrekturen für die Funktionsaufrufe, die im Supervisor-Modus ausgeführt wurden, erst danach gemacht. Dadurch wird der Stack beschädigt und spätestens beim nächsten return fliegt einem das ganze mit Bömbchen um die Ohren.

Glücklicherweise gibt es eine Lösung, ohne dass man diese sinnvolle Optimierung ganz abschalten muss. Man deklariert die Funktion, in der die Super-Aufrufe sind, als

void foobar(void) __attribute__ ((optimize ("no-defer-pop")));
und aktiviert somit nur lokal für diese Funktion die sofortige Stackkorrektur nach jedem Funktionsaufruf.

Noch besser wäre es natürlich, könnte man gcc mitteilen, dass Super eine Funktion ist, die den Stack modifiziert und deshalb alle Stackkorrekturen nur bis zum nächsten Aufruf von Super gesammelt werden können. Eine Art Barriere also. Weiß jemand, ob das geht?

[1] http://forum.atari-home.de/index.php?topic=13002.msg208583#msg208583

Offline czietz

  • Benutzer
  • Beiträge: 3.686
Re: gcc, GEMDOS Super und Stackzerstörung
« Antwort #1 am: Sa 06.08.2016, 16:57:24 »
OK, nach einem Hinweis, gefunden mit Google, dass gcc (sinnigerweise) vor einem goto alle noch ausstehenden Stack-Korrekturen durchführt, habe ich mir folgende Konstruktion basteln können:

#ifdef __GNUC__
/* BARRIER2 is here to expand __LINE__. */
#define BARRIER3(x) goto _barrier##x; _barrier##x:
#define BARRIER2(x) BARRIER3(x)
#define BARRIER BARRIER2(__LINE__)
#else
#define BARRIER
#endif

Ein BARRIER jeweils von den Super-Aufrufen sorgt nun dafür, dass der Stack heile bleibt. Das unnütze goto wird später vom Optimierer wieder entfernt. Schön ist das aber auch nicht...

Offline simonsunnyboy

  • Moderator
  • *****
  • Beiträge: 1.807
  • Rock'n'Roll is the thing - Jerry Lee is the king!
Re: gcc, GEMDOS Super und Stackzerstörung
« Antwort #2 am: Sa 06.08.2016, 17:04:19 »
Also nocheinfacher ohne gcc Eigenschaften, packe deinen Code in eine eigene Funktion und rufe diese über XBIOS Supexec() auf. Da kümmert sich das TOS selbst um Stack und alles, das ist Compilerunabhängig.  Geht mit jeder void ...(void) Funktion
Paradize - ST Offline Tournament
Stay cool, stay Atari!
1x2600jr, 1x1040STFm, 1x1040STE 4MB+TOS2.06+SatanDisk, 1xF030 14MB+FPU+NetUS-Bee

Offline mfro

  • Benutzer
  • Beiträge: 1.640
Re: gcc, GEMDOS Super und Stackzerstörung
« Antwort #3 am: Sa 06.08.2016, 17:45:36 »
Um Stack-Fehler zu vermeiden, gibt's in den Bindings (osbind.h) die SuperToUser()-Funktion, die den Stackpointer explizit setzt. Ich hab' mir jetzt deinen Fall nicht genau angesehen, nehme aber einfach mal an, daß der auch abgedeckt wird.

Supexec() ist aber tatsächlich in den meisten Fällen die bessere Wahl.
And remember: Beethoven wrote his first symphony in C

Offline czietz

  • Benutzer
  • Beiträge: 3.686
Re: gcc, GEMDOS Super und Stackzerstörung
« Antwort #4 am: Sa 06.08.2016, 17:58:02 »
SuperToUser() dürfte tatsächlich eine Lösung sein. Supexec() ist hier nicht so toll, weil ich -- anders im Beispiel mit foobar() -- schon Parameter an die Funktion übergeben will, am liebsten ohne globale Variablen dafür zu verwenden.

Aber das Experiment ist ohnehin mehr oder weniger beendet, weil gcc (selbst mit -O2) unglaublich ineffizienten Code generiert. Die "Moving Inversions" in YAART brauchen ca. 50%(!) länger als in der mit Pure C compilierten Version. Darin werden keine Bibliotheksfunktionen aufgerufen, es ist also wirklich der generierte Code.


Offline mfro

  • Benutzer
  • Beiträge: 1.640
Re: gcc, GEMDOS Super und Stackzerstörung
« Antwort #5 am: Sa 06.08.2016, 18:14:58 »
Aber das Experiment ist ohnehin mehr oder weniger beendet, weil gcc (selbst mit -O2) unglaublich ineffizienten Code generiert. Die "Moving Inversions" in YAART brauchen ca. 50%(!) länger als in der mit Pure C compilierten Version. Darin werden keine Bibliotheksfunktionen aufgerufen, es ist also wirklich der generierte Code.

Das interessiert mich, kannst Du den Code mal zeigen?
And remember: Beethoven wrote his first symphony in C

Offline czietz

  • Benutzer
  • Beiträge: 3.686
Re: gcc, GEMDOS Super und Stackzerstörung
« Antwort #6 am: Sa 06.08.2016, 18:59:33 »
Ich gucke mal, ob ich den Code soweit herunterbrechen kann, dass ich ihn hier posten kann. Das wird aber nur niedrige Priorität haben, mit Pure C bin ich soweit glücklich, dass ich ehrlich gesagt keine große Energie in Ursachenanalyse mit dem gcc stecken will.

Offline mfro

  • Benutzer
  • Beiträge: 1.640
Re: gcc, GEMDOS Super und Stackzerstörung
« Antwort #7 am: Sa 06.08.2016, 19:07:19 »
... Geht mit jeder void ...(void) Funktion

Nicht void. Supexec() erwartet eine Funktion, die einen long-Wert zurückgibt. Den serviert sie dann auch dem Aufrufer.
And remember: Beethoven wrote his first symphony in C

Offline simonsunnyboy

  • Moderator
  • *****
  • Beiträge: 1.807
  • Rock'n'Roll is the thing - Jerry Lee is the king!
Re: gcc, GEMDOS Super und Stackzerstörung
« Antwort #8 am: Sa 06.08.2016, 20:22:29 »
Offiziell ja, inoffiziell geht es genauso auch gut mit void ;)
Paradize - ST Offline Tournament
Stay cool, stay Atari!
1x2600jr, 1x1040STFm, 1x1040STE 4MB+TOS2.06+SatanDisk, 1xF030 14MB+FPU+NetUS-Bee

Offline czietz

  • Benutzer
  • Beiträge: 3.686
Re: gcc, GEMDOS Super und Stackzerstörung
« Antwort #9 am: Sa 06.08.2016, 21:29:54 »
Das interessiert mich, kannst Du den Code mal zeigen?

Anbei eine deutlich reduzierte Version der "Moving Inversions" aus YAART zum Testen. Hier benötigt die gcc-Version immer noch 25% länger als die Pure-C-Version. yrt_tim.prg ist mit Pure C compiliert, yrt_tim.tos mit gcc -O2.

Profiling mit Hatari zeigt, dass sich Pure C deutlich schlauer anstellt, was die Adressen von häufig vorkommenden Speicherzugriffen angeht und sie in ein Adressregister packt. gcc greift auch in einer Schleife jedesmal auf die Adresse direkt zu und braucht so z.B. mit...
move.w    $160dc,d0
move.w    d0,(a0)+

... viel länger als Pure C mit einmalig initialisiertem A3 und ...
move.w    (a3),(a0)+.

6 304 768 Zyklen vs. 3 151 944 Zyklen!

Offline czietz

  • Benutzer
  • Beiträge: 3.686
Pure C vs. gcc -- Wer ist schneller
« Antwort #10 am: So 07.08.2016, 10:48:10 »
... und bevor jemand kommentiert "Ja, aber globale Variablen benutzt man ja auch nicht": Wenn ich alle Variablen der Funktion als Argument übergebe, wird es zwar generell schneller, weil diese dann sowohl bei Pure C als auch bei gcc in Registern vorgehalten werden, jedoch ist der mit gcc generierte Code immer noch ca. 20% langsamer als der mit Pure C generierte.

Insbesondere braucht diese Schleife mit gcc ca. 40%(!) länger als mit Pure C.

while (p>start_local) {
if ((x = *(--p)) != patt2_local) {
bad = x;
}
*p = patt1_local;
}

Ich hätte ja gedacht, dass ein moderner Compiler gegen einen aus dem vergangenen Jahrtausend meilenweit überlegen ist. Wohl falsch gedacht...

Offline simonsunnyboy

  • Moderator
  • *****
  • Beiträge: 1.807
  • Rock'n'Roll is the thing - Jerry Lee is the king!
Re: gcc, GEMDOS Super und Stackzerstörung
« Antwort #11 am: So 07.08.2016, 11:44:56 »
O3 hilft vielleicht noch mehr?
« Letzte Änderung: So 07.08.2016, 11:46:59 von simonsunnyboy »
Paradize - ST Offline Tournament
Stay cool, stay Atari!
1x2600jr, 1x1040STFm, 1x1040STE 4MB+TOS2.06+SatanDisk, 1xF030 14MB+FPU+NetUS-Bee

Offline czietz

  • Benutzer
  • Beiträge: 3.686
Pure C vs. gcc -- Wer ist schneller
« Antwort #12 am: So 07.08.2016, 12:01:10 »
Mit -O3 braucht die gepostete Schleife mit gcc nur noch 15% länger als mit Pure C. Immer noch nicht toll.

gcc -O3 hält den Pointer p in zwei Adressregistern vor, einen zum Lesen und Beschreiben der Speicherstelle und einen zum Vergleich mit start. So müssen beide Register in jeder Schleife synchronisiert werden und das kostet unnötig Zeit.

Offline Börr

  • Benutzer
  • Beiträge: 859
Re: gcc, GEMDOS Super und Stackzerstörung
« Antwort #13 am: So 07.08.2016, 17:56:01 »
Könnten wir mal ein Atari Coding Workshop Wochenende machen?

Offline mfro

  • Benutzer
  • Beiträge: 1.640
Re: Pure C vs. gcc -- Wer ist schneller
« Antwort #14 am: Fr 12.08.2016, 16:56:25 »
Mit -O3 braucht die gepostete Schleife mit gcc nur noch 15% länger als mit Pure C. Immer noch nicht toll.

gcc -O3 hält den Pointer p in zwei Adressregistern vor, einen zum Lesen und Beschreiben der Speicherstelle und einen zum Vergleich mit start. So müssen beide Register in jeder Schleife synchronisiert werden und das kostet unnötig Zeit.

Hmmm.

Mit -O3:

Zitat
Took 185 Ticks.

mit -O2:

Zitat
Took 204 Ticks.

mit -Os:

Zitat
Took 171 Ticks


... da scheint was mit der Optimierung im Argen zu sein ...
And remember: Beethoven wrote his first symphony in C

Offline czietz

  • Benutzer
  • Beiträge: 3.686
Re: Pure C vs. gcc -- Wer ist schneller
« Antwort #15 am: Fr 12.08.2016, 17:49:05 »
Du hast einen schnelleren Rechner als ich  ;). Auf einem 1040ST (original mit 8 MHz) bekomme ich:

gcc -O02579 ticks
gcc -O11424 ticks
gcc -O21155 ticks
gcc -O31048 ticks
gcc -Os994 ticks (schneller als O3, wie bei Dir)
Pure C940 ticks (Gewinner!)

Die -- nicht im Forum gepostete -- Variante mit lokalen statt mit globalen Variablen liefert:

gcc -O02365 ticks
gcc -O1779 ticks
gcc -O2806 ticks (langsamer als O1!)
gcc -O3725 ticks
gcc -Os672 ticks
Pure C672 ticks (gleichauf mit gcc -Os)

Zitat
... da scheint was mit der Optimierung im Argen zu sein ...

Das sehe ich auch so.

Offline mfro

  • Benutzer
  • Beiträge: 1.640
Re: Pure C vs. gcc -- Wer ist schneller
« Antwort #16 am: Fr 12.08.2016, 23:14:23 »
Du hast einen schnelleren Rechner als ich  ;). Auf einem 1040ST (original mit 8 MHz)...

... einen (fast) originalen TT.

Lustigerweise wird's da auch mit -O3 -mcpu=68030 -mtune=68030 nur unwesentlich schneller, das mit Abstand beste Ergebnis liefert auch da der 68000 code mit -Os ...
And remember: Beethoven wrote his first symphony in C

Offline simonsunnyboy

  • Moderator
  • *****
  • Beiträge: 1.807
  • Rock'n'Roll is the thing - Jerry Lee is the king!
Re: gcc, GEMDOS Super und Stackzerstörung
« Antwort #17 am: Sa 13.08.2016, 10:50:56 »
Kurz und wenig Befehle kann auch schnell sein, muss nicht automatisch, aber kann....
Paradize - ST Offline Tournament
Stay cool, stay Atari!
1x2600jr, 1x1040STFm, 1x1040STE 4MB+TOS2.06+SatanDisk, 1xF030 14MB+FPU+NetUS-Bee

Offline ari.tao

  • Benutzer
  • Beiträge: 2.248
  • Gesperrter User
Re: gcc, GEMDOS Super und Stackzerstörung
« Antwort #18 am: So 14.08.2016, 08:41:57 »
ad Topic:
SuperToUser() dürfte tatsächlich eine Lösung sein. Supexec() ist hier nicht so toll, weil ich -- anders im Beispiel mit foobar() -- schon Parameter an die Funktion übergeben will, am liebsten ohne globale Variablen dafür zu verwenden.
Man kann SuperExec (XBIOS_38) grundsätzlich auch für Prozeduren mit Parametern verwenden. Falls es noch interessiert, kann ich ein Bsp. für M2(TDI) zeigen, mit C kenne ich mich nicht aus. Alles nur eine Frage des Bindings. Übrigens kann man auch mittels GEMDOS_32 ein SuperExec ´zusammenbinden´.

ad offTopic:
Mit großem Interesse verfolge ich, wie Eure C-Compiler ´ticken´ . Weiß jmd., wie gcc die genannten ´ticks´ berechnet? Würde mich stark interessieren! Ich habe mich mal sehr intensiv mit Takt-zählen für Code-Stücke befaßt. Auf den kleinen STs ist das ganz einfach (man schaut in´s MC68000-Manual und addiert), auf einer 68030-Maschine ungleich schwieriger! Der Proz.-Cache sowie die unterschiedlichen MHze für Bus, P.-extern & P.-intern machen eine Berechnung nur an Hand des Codes imho unmöglich! Ich habe daraufhin eine Meßmethode programmiert. Funzt grundsätzlich, aber die Sache ist derart kompliziert, daß ich bis heutigen Tags nicht sicher bin, ob alles ok ist.
Du hast einen schnelleren Rechner als ich  ;). Auf einem 1040ST (original mit 8 MHz)...
... einen (fast) originalen TT.
Lustigerweise wird's da auch mit -O3 -mcpu=68030 -mtune=68030 nur unwesentlich schneller, das mit Abstand beste Ergebnis liefert auch da der 68000 code mit -Os ...
Die Performance kann man afaik immer nur für das Gesamt-System vergleichen, also die Kombi Maschine + Compiler. Das macht man (auf Ataris) mit dem Dhrystone-Test. Die ´ticks´ alleine dürften ziemlich irreführend sein!

ad @mfro :
Könnte sich Deine Biene, bitte bitte, auch mal setzen? Das Viech ist derart lästig, daß ich mich davon immer in den H... gestochen fühle  :'(

Falcon+ddd32MHz, TT+CrazyDotsGK und noch ein paar andere.

Offline czietz

  • Benutzer
  • Beiträge: 3.686
Re: gcc, GEMDOS Super und Stackzerstörung
« Antwort #19 am: So 14.08.2016, 10:32:59 »
Man kann SuperExec (XBIOS_38) grundsätzlich auch für Prozeduren mit Parametern verwenden. Falls es noch interessiert, kann ich ein Bsp. für M2(TDI) zeigen, mit C kenne ich mich nicht aus. Alles nur eine Frage des Bindings.

Auch wenn das Problem mittlerweile gelöst ist, würde mich das Beispiel (auch wenn's in Modula 2 ist) interessieren.

ad offTopic:
Mit großem Interesse verfolge ich, wie Eure C-Compiler ´ticken´ . Weiß jmd., wie gcc die genannten ´ticks´ berechnet? Würde mich stark interessieren!

Die "ticks", die mein Testprogramm auswirft, sind einfach aus dem 200-Hz-Timer (Systemvariable hz_200) abgeleitet und somit proportional zur Zeit, die für die Ausführung des "Moving inversions"-Algorithmus benötigt wird. Das ist ja das, was letztlich der Benutzer (von YAART) merkt: Wieviel Zeit vergeht für einen Testdurchlauf. Und das dauert in der gcc-compilierten Version eben länger als in der Pure-C-compilierten Version.

Wenn ich oben hingegen von Zyklen gesprochen habe, waren CPU-Taktzyklen gemeint und sie waren mit dem Profiler von Hatari aufgenommen. Für die Emulation eines MC68000, dürfte sie ziemlich genau der Realität entsprechen. Für einen MC68030 weiß ich es nicht, wobei ich seit YAART immerhin weiß, dass Hatari durchaus den Cache des MC68030 emulieren kann.

Zitat
Die Performance kann man afaik immer nur für das Gesamt-System vergleichen, also die Kombi Maschine + Compiler. Das macht man (auf Ataris) mit dem Dhrystone-Test. Die ´ticks´ alleine dürften ziemlich irreführend sein!

Das denke ich nicht. Die ticks (aka die vergangene Zeit) ist genau das, was der Benutzer spürt, siehe oben.