Forum: Compiler & IDEs avr-gcc: -fsplit-wide-types ja oder nein?


von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Neuere Versionen von avr-gcc kennen den Schalter
1
-fsplit-wide-types
der bei angeschalteter Optimierung automatisch aktiviert wird.

Durch
1
-fno-split-wide-types
kann der entsprechende Optimierungs-Pass in gcc deaktiviert werden.

Mit interessiert nun, welche Ergebnisse bei euch mit bzw. ohne diese 
Optimierung erzielt werden, und welche Verbesserungen mit/ohne diese 
Optimierung in "echten" Projekten beobachtet werden.

Hintergrund der Frage ist, ob es sinnvoll ist, den entsprechende 
Optimierungs-Pass für avr-gcc zu deaktivieren. Mit einem expliziten 
-fsplit-wode-types wäre er immer nocht per Kommandozeile aktivierbar.

Eine ggf. Umstellung würde frühestens in avr-gcc 4.7.0 erfolgen, dessen 
Release ca. Frühjahr 2012 sein dürfte.

Da ich selbst keine allzu große Code-Basis habe, bin ich an 
Erfahrungsberichten interessiert.

Für mein letztes Projekt, übersetzt mit avr-gcc 4.5.0, ergibt sich ein 
Unterschied von 14960 Bytes zu 14952 Bytes, also ein Flash-Gewinn von 
schlappen 8 Bytes.

Zum Hintergrund: Der Schalter bewirkt, daß ein Optimierungs-Pass läuft, 
der versucht, große Typen wie 32-Bit Variablen in 8-Bit-Stücke 
aufzuteilen. Je nach Code gelingt das für die gesamte Lebensdauer eines 
Bytes, und wenn z.B. nachgewiesen werden kann, daß das Byte nicht 
benötigt wird, können entsprechende Befehle entfernt weden.

Problem bei avr-gcc ist, daß viele Operationen nicht auf Byte-Große 
zerlegt werden können. Z.B. kann eine 32-Bit-Addition nicht in 4 
Teiladditionen zerlegt werden: die interne Darstellung einer 32-Bit 
Addition in avr-gcc verwendet 32-Bit Objekte.

Dies führt dazu, daß die 32-Bit Objete/Operationen teilweise in 8-Bit 
Teile zerlegt werden, teilweise nicht, und es einen Mix dieser beiden 
Darstellungen gibt, die dem avr-Backend weitere Optimierungen 
erschweren.

Ohne das Zerbröseln in 8-Bit Subtypen hat der avr-Teil in gcc etwas mehr 
Möglichkeiten, selbst lowlevel-Optimierungen vorzunehmen. Das Potential 
hierzu ist allerdings nicht ausgeschöpft -- vornehmlich auch deshalb, 
weil -fsplit-wide-types standardmässig aktiviert ist, und das avr 
Backend teilweise ganz andere Pattern zu sehen bekommt abhängig davon, 
ob der Schalter aktiv ist oder nicht.

Wie sieht's also in euren Projekten aus, wenn ihr die Codegüte mit/ohne 
diesen Schalter vergleicht? Am aussagekräftigsten sind natürlich 
Berichte mit relativ jungen avr-gcc Releases wie 4.5.x oder 4.6.x

Johann

von Klaus W. (mfgkw)


Lesenswert?

Ich habe gerade ein kleines C++-Programm zur Hand (hauptsächlich etwas 
Gefummel mit LCD-Ausgabe); das hat mit und ohne -fsplit-wide-types
gleichermaßen 6704 Bytes (.text) bei -Oc.
Bei -O3 hat es jeweils 13010 Byte, mit -O2 jeweils 13020 Bytes
und mit -O1 jeweils 12986 Bytes.

Effekt also exakt: 0

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

-fsplit-wide-types wird standardmässig gesetzt; es nochmals explizit 
hinzuschreiben hat also keinen Effekt.

Was Effekt hat, ist den Schalter explizit zu deaktivieren, und das 
geht mittels -fno-split-wide-types, was den Standard überschreibt.

von Klaus W. (mfgkw)


Lesenswert?

1
         -fsplit-wide-types -fno-split-wide-types
2
  -Os        6704               6704
3
  -O0       15174              15036
4
  -O1       12986              12978
5
  -O2       13020              13020
6
  -O3       13010              13024

Das Programm rechnet nicht viel, fummelt großteils mit Bitschieben rum 
(LCD44780).

von 900ss (900ss)


Lesenswert?

Programm für eine Nixieuhr:
       -fsplit-wide-types    -f-nosplit-wide-types
-Os:       12370                 12344

Noch 'n Uhrenprogramm :-)
       -fsplit-wide-types    -f-nosplit-wide-types
-O0:       6124                 6124

Lötstation:
       -fsplit-wide-types    -f-nosplit-wide-types
-O2:       16502                 16524

Testprogramm mit RTOS uc/OS-II:
       -fsplit-wide-types    -f-nosplit-wide-types
-O0:    13006                  13196
-O2:     7902                   7902
-Os:     7544                   7546

avr-gcc --version
avr-gcc (WinAVR 20100110) 4.3.3
Neue AVR-GCCs hab ich nicht. :-/

von Klaus W. (mfgkw)


Lesenswert?

PS: mein avr-gcc ist 4.3.2

von Yalu X. (yalu) (Moderator)


Lesenswert?

Ich habe mal ein Stück Real-Life-Code durch alle bei mir installierten
AVR-GCC-Releases geschoben. Ein 4.6.x-Release hatte ich noch nicht und
leider auch keins bei GNU gefunden, weder auf dem FTP- noch dem SVN-
Server. Also habe ich den SVN-Trunk genommen, der hat aber schon 4.7.0
gebaut. Da aber 4.7.x gerade erst begonnen wurde, schätze ich, dass die
angegebenen Werte weitgehend dem (hoffentlich in den nächsten Tagen
veröffentlichten) 4.6.0-Release entsprechen.
1
       text-Größe ungelinkt (ohne Bibliotheken) -O2
2
        -fno-split-wide-types  -fsplit-wide-types
3
—————————————————————————————————————————————————
4
4.3.5            3646                 3646
5
4.4.2            3636                 3884
6
4.5.2            3628                 3918
7
4.6.0/4.7.0      3630                 4076
8
—————————————————————————————————————————————————
9
10
Zum Vergleich:
11
12
—————————————————————————————————————————————————
13
4.2.4                       3628
14
—————————————————————————————————————————————————

Wie man sieht, wird bei der Defaulteinstellung der Code mit jeder Ver-
sion größer, mit -fno-split-wide-types bleibt die Größe in etwa gleich.

Heißt das etwa, dass diese Wide-Types-Optimierung die Hauptursache für
die schlechtere Codegrößenoptimierung der neueren GCC-Versionen war?
Genau diese war nämlich der Grund dafür, dass ich mich nie so richtig
von der 4.2.x-Version trennen konnte und dass ich so viele Versionen
parallel auf meinem Rechner installiert habe :)

Aber danke für den Hinweis!

Ich werde diese "Optimierung" künftig explizit ausschalten, mir bei
Gelegenheit aber noch ein paar Code-Beispiele anschauen, um besser zu
verstehen, was der Compiler da tut und wann das Ausschalten vielleicht
doch nicht ratsam ist. Falls ich dabei neue Erkenntnisse gewinne, werde
ich sie hier posten.

von Yalu X. (yalu) (Moderator)


Lesenswert?

Mist, ich wollte eigentlich mit -Os testen, habe aber aus Versehen -O2
eingestellt gehabt. So sehen die Ergebnisse für -Os aus:
1
       text-Größe ungelinkt (ohne Bibliotheken) -Os
2
        -fno-split-wide-types  -fsplit-wide-types
3
—————————————————————————————————————————————————
4
4.3.5            3482                 3482
5
4.4.2            3484                 3590
6
4.5.2            3581                 3727
7
4.6.0/4.7.0      3524                 3700
8
—————————————————————————————————————————————————
9
10
Zum Vergleich:
11
12
—————————————————————————————————————————————————
13
4.2.4                       3438
14
—————————————————————————————————————————————————

Jetzt sind die Unterschiede nicht mehr ganz so groß, aber doch noch
erkennbar.

Ich habe das -O2 im vorherigen Beitrag in der Tabelle nachgetragen,
damit keine Missverständnisse enstehen.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Schon mal Danke für die Daten!

Yalu X. schrieb:
> Ich habe mal ein Stück Real-Life-Code durch alle bei mir installierten
> AVR-GCC-Releases geschoben. Ein 4.6.x-Release hatte ich noch nicht und
> leider auch keins bei GNU gefunden, weder auf dem FTP- noch dem SVN-
> Server.

Getagt ist die 4.6.0 noch nicht, wird noch'n bisschen dran rumgewienert. 
Ist der Trunk unter
1
/trunk
dann ist der 4.6-Zweig unter
1
/branches/gcc-4_6-branch
und die 4.6.0-Release wird schlieslich gelegt nach
1
/tags/gcc_4_6_0_release

> Also habe ich den SVN-Trunk genommen, der hat aber schon 4.7.0 gebaut.
> Da aber 4.7.x gerade erst begonnen wurde, schätze ich, dass die
> angegebenen Werte weitgehend dem (hoffentlich in den nächsten Tagen
> veröffentlichten) 4.6.0-Release entsprechen.

In der 4.7 gab's tatsächlich schon ne Kostenanpassung, die Auswirkung 
auf die Codegröße hat mit Hinweisen auf ca -1%.

1
>         -fno-split-wide-types  -fsplit-wide-types
2
> —————————————————————————————————————————————————
3
> 4.6.0/4.7.0      3630                 4076
4
> —————————————————————————————————————————————————
5
>

-4.5% ist ja schon sehr deutlich; so viel hätte ich da nicht erwartet...

> Wie man sieht, wird bei der Defaulteinstellung der Code mit jeder Ver-
> sion größer, mit -fno-split-wide-types bleibt die Größe in etwa gleich.
>
> Heißt das etwa, dass diese Wide-Types-Optimierung die Hauptursache für
> die schlechtere Codegrößenoptimierung der neueren GCC-Versionen war?

Schlecht zu sagen. Jedenfalls ist in den Dumps zu sehen, daß dieser Pass 
(potentiellen) handgeklöppelten Optimierungen im avr-Backend im Wege 
steht.

Ein weiterer Grund ist der neue Register-Allokator. An sich ist der ganz 
ok, allerding macht das avr-Backend an vielen Stellen Implikationen an 
den alten Allokator und dessen Verhalten. Z.B. wird ihm eine 
Adressierungsart wie X+const angeboten (ursprünglich um ein paar 
Corner-Cases in den Griff zu bekommen, die dann per adiw/ld/sbiw 
umgesezt wird), die AVR überhaupt nicht hat.

Ich hab schon versucht, dem Backend das abzugewöhnen und einige neue 
Hooks zu verwenden. Bei extremer Registerlast in einem sehr 
longlong-lastigen Testfall steigt gcc aber aus, weil ihm die Register 
ausgehen... Ansonsten kommt der Code näher an "vernünftigen" AVR-Code 
ran.

> Genau diese war nämlich der Grund dafür, dass ich mich nie so richtig
> von der 4.2.x-Version trennen konnte und dass ich so viele Versionen
> parallel auf meinem Rechner installiert habe :)

Geht mir so mit der 3.4.6. Für meine privaten Mini-Projekte tut der am 
besten.

> Ich werde diese "Optimierung" künftig explizit ausschalten, mir bei
> Gelegenheit aber noch ein paar Code-Beispiele anschauen, um besser zu
> verstehen, was der Compiler da tut und wann das Ausschalten vielleicht
> doch nicht ratsam ist. Falls ich dabei neue Erkenntnisse gewinne, werde
> ich sie hier posten.

Gerne :-)

von Yalu X. (yalu) (Moderator)


Lesenswert?

Johann L. schrieb:
> Getagt ist die 4.6.0 noch nicht, wird noch'n bisschen dran rumgewienert.
> Ist der Trunk unter
>
> /trunk
>
> dann ist der 4.6-Zweig unter
>
> /branches/gcc-4_6-branch
>
> und die 4.6.0-Release wird schlieslich gelegt nach
>
> /tags/gcc_4_6_0_release

Ich habe zunächst nur nach /tags/gcc_4_6_0_release gesucht. Auf die
Idee, auch in den Branches nachzuschauen, bin ich erst gekommen, nachdem
ich den obigen Beitrag schon geschrieben hatte. Da mit meinem nur mäßig
schnellen Internetzugang der Checkout eine halbe Ewigkeit dauert, habe
ich es dabei bewenden lassen. Oder kann man mit Subversion einen Branch
in eine bestehende Arbeitskopie so outchecken oder updaten, dass nur
geänderte Dateien vom Server geholt werden?

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Yalu X. schrieb:

> Da mit meinem nur mäßig
> schnellen Internetzugang der Checkout eine halbe Ewigkeit dauert, habe
> ich es dabei bewenden lassen. Oder kann man mit Subversion einen Branch
> in eine bestehende Arbeitskopie so outchecken oder updaten, dass nur
> geänderte Dateien vom Server geholt werden?

Oje, da ist ja alles mögliche Gerüffel wie java, ada, testsuite, 
libstdc++ dabei...

Eine Möglichkeit wäre svn switch.

Da sich 4.6 und 4.7 noch kaum unterscheiden, kann man auch lediglich das 
avr-Backend updaten, z.B. per
1
svn update -rHEAD
in /gcc/config/avr/

Der letzte 4.7 Snapshot ist 171148, latest 4.6 Snapshot ist 171171.

Eine Hack, um schneller zu updaten, ist unter "Optimize disk usage" in 
http://gcc.gnu.org/wiki/SvnSetup aufgezeigt. Dort werden nicht 
verwendete Verzeichnisse ins Nirvana gemappt.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Ich habe mal eine einzelne Konfiguration des µracoli-Projekts
mit beiden Varianten compiliert:
1
--- /tmp/default        2011-03-21 12:03:40.000000000 +0100
2
+++ /tmp/nowide 2011-03-21 12:07:01.000000000 +0100
3
@@ -1,19 +1,19 @@
4
    text           data     bss     dec     hex filename
5
     264              0       0     264     108 install/bin/albo_zgbt1281a2uart0.elf
6
-   6416            136     334    6886    1ae6 install/bin/diag_zgbt1281a2uart0.elf
7
-  10676            108     632   11416    2c98 install/bin/rdiag_zgbt1281a2uart0.elf
8
-  10324            130    1846   12300    300c install/bin/sniffer_zgbt1281a2uart0.elf
9
+   6412            136     334    6882    1ae2 install/bin/diag_zgbt1281a2uart0.elf
10
+  10640            108     632   11380    2c74 install/bin/rdiag_zgbt1281a2uart0.elf
11
+  10300            130    1846   12276    2ff4 install/bin/sniffer_zgbt1281a2uart0.elf
12
    1922              0      15    1937     791 install/bin/wgpio_zgbt1281a2uart0.elf
13
    1224             36     399    1659     67b install/bin/wibo_zgbt1281a2uart0.elf
14
-   7814            356     702    8872    22a8 install/bin/wibohost_zgbt1281a2uart0.elf
15
-   7952             32     613    8597    2195 install/bin/wuart_zgbt1281a2uart0.elf
16
+   7804            356     702    8862    229e install/bin/wibohost_zgbt1281a2uart0.elf
17
+   7926             32     613    8571    217b install/bin/wuart_zgbt1281a2uart0.elf
18
     270              0       0     270     10e install/bin/xmpl_dbg_zgbt1281a2uart0.elf
19
-   3268             22     261    3551     ddf install/bin/xmpl_hif_echo_zgbt1281a2uart0.elf
20
-   3900            116     261    4277    10b5 install/bin/xmpl_hif_zgbt1281a2uart0.elf
21
-   3792              8     361    4161    1041 install/bin/xmpl_linbuf_rx_zgbt1281a2uart0.elf
22
-   2726            178      10    2914     b62 install/bin/xmpl_linbuf_tx_zgbt1281a2uart0.elf
23
-   6548             24     478    7050    1b8a install/bin/xmpl_radio_range_zgbt1281a2uart0.elf
24
-   4856             22     134    5012    1394 install/bin/xmpl_radio_stream_zgbt1281a2uart0.elf
25
+   3264             22     261    3547     ddb install/bin/xmpl_hif_echo_zgbt1281a2uart0.elf
26
+   3886            116     261    4263    10a7 install/bin/xmpl_hif_zgbt1281a2uart0.elf
27
+   3762              8     361    4131    1023 install/bin/xmpl_linbuf_rx_zgbt1281a2uart0.elf
28
+   2700            178      10    2888     b48 install/bin/xmpl_linbuf_tx_zgbt1281a2uart0.elf
29
+   6522             24     478    7024    1b70 install/bin/xmpl_radio_range_zgbt1281a2uart0.elf
30
+   4830             22     134    4986    137a install/bin/xmpl_radio_stream_zgbt1281a2uart0.elf
31
     418              0       4     422     1a6 install/bin/xmpl_timer_zgbt1281a2uart0.elf
32
     984              0       2     986     3da install/bin/xmpl_trx_base_zgbt1281a2uart0.elf
33
    1008              0     130    1138     472 install/bin/xmpl_trx_echo_zgbt1281a2uart0.elf

Fazit: nicht bei allen Applikationen ändert sich was, aber wenn sich
was ändert, dann ist der Code mit -fno-split-wide-types in jedem
Falle kleiner geworden.

von Yalu X. (yalu) (Moderator)


Lesenswert?

Das Beispiel, das ich gestern gepostet habe, verwendet an einigen
Stellen FP-Arithmetik. Speziell die GCC-Version 4.7.0 scheint damit
leichte Probleme zu haben. Folgender Code
1
double g(double x, double y);
2
3
double f(double x, double y) {
4
  return g(x, y);
5
}

braucht bei 4.7.0 mit -Os defaultmäßig 18 Bytes mit
-fno-split-wide-types nur 6 Bytes. Das ist Faktor 3 ;-)

Im Assemblercode sieht man sofort, warum:
1
f:
2
  call g
3
  mov r20,r22
4
  mov r21,r23
5
  mov r22,r24
6
  mov r23,r25
7
  movw r24,r22
8
  movw r22,r20
9
  ret

Die MOV/MOVW-Orgie tut nichts, außer unnötigerweise zwei zusätzliche
Register zu beschreiben und Taktzyklen zu verbrauchen.

Mit -fno-split-wide-types oder 4.5.2 oder %s/double/long/g sieht das
Ergbnis anständig aus:
1
f:
2
  call g
3
  ret

Da Optimum (nur 4 Bytes) wäre
1
f:
2
  jmp g

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Yalu X. schrieb:

> Das Optimum (nur 4 Bytes) wäre
>
>
1
> f:
2
>   jmp g
3
>

-mrelax?

von Yalu X. (yalu) (Moderator)


Lesenswert?

Jörg Wunsch schrieb:
> -mrelax?

Ja, das funktioniert. Allerdings wird dadurch der CALL zwar durch einen
JMP oder RJMP ersetzt, der RET bleibt aber trotzdem stehen. Es werden
also Zyklen, aber nicht unbedingt Bytes eingespart.

Dass der RET stehenbleibt, mag damit zusammenhängen, dass -mrelax nicht
vom Compiler, sondern vom Linker bearbeitet wird. Aber warum kann der
Compiler nicht selbst die CALL-RET-Kombination durch einen JMP ersetzen?
Bei Endrekursionen hat man ja fast die gleiche Situation, und die werden
direkt vom Compiler optimiert.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Yalu X. schrieb:
> Jörg Wunsch schrieb:
>> -mrelax?
>
> Ja, das funktioniert. Allerdings wird dadurch der CALL zwar durch einen
> JMP oder RJMP ersetzt, der RET bleibt aber trotzdem stehen. Es werden
> also Zyklen, aber nicht unbedingt Bytes eingespart.
>
> Dass der RET stehenbleibt, mag damit zusammenhängen, dass -mrelax nicht
> vom Compiler, sondern vom Linker bearbeitet wird. Aber warum kann der
> Compiler nicht selbst die CALL-RET-Kombination durch einen JMP ersetzen?
> Bei Endrekursionen hat man ja fast die gleiche Situation, und die werden
> direkt vom Compiler optimiert.

Ein entsprechendes Patch ist bereits abgesegnet, aber noch nicht 
eingespielt. Das kann einen Tail-Call auch dann optimieren, wenn 
Register gepusht werden.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Jörg Wunsch schrieb:
> Ich habe mal eine einzelne Konfiguration des µracoli-Projekts
> mit beiden Varianten compiliert:
> [...]
> Fazit: nicht bei allen Applikationen ändert sich was, aber wenn sich
> was ändert, dann ist der Code mit -fno-split-wide-types in jedem
> Falle kleiner geworden.

Welche gcc-Version wars?

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Johann L. schrieb:

> Welche gcc-Version wars?

4.3.4

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Yalu X. schrieb:
1
> f:
2
>   call g
3
>   ret
4
>
>
> Das Optimum (nur 4 Bytes) wäre
>
>
1
> f:
2
>   jmp g
3
>

Das kann jetzt auch avr-gcc :-)

Mit dem -fno-split-wide-types sieht's leider nicht so gut aus. In 
Funktionen der libgcc wird der Code teilweise drastisch größer oder 
drastisch kleiner mit dem Schalter, also ein uneinheitliches Bild. Der 
größere Code ist vor allem bei 64-Bit Routinen, die nicht sooo 
praxisrelevant sind... Aber das macht so eine Umstellung viel schwerer 
zu begründen.

von Yalu X. (yalu) (Moderator)


Lesenswert?

Johann L. schrieb:
>>> f:
>>   jmp g
>>
> Das kann jetzt auch avr-gcc :-)

Das ging aber schnell. Ich hoffe, das Feature wurde nicht extra wegen
meines Beitrags von oben eingebaut ;-)

In welcher Version? Noch in 4.6 oder erst in 4.7?

> Mit dem -fno-split-wide-types sieht's leider nicht so gut aus.

Das ist auch nicht so schlimm. Wichtig ist es zu wissen, dass es da eine
Option gibt, bei der das Herumspielen u.U. lohnt. Die Defaulteinstellung
ist dann eher zweitrangig.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Yalu X. schrieb:
> Johann L. schrieb:
>>>> f:
>>>   jmp g
>>>
>> Das kann jetzt auch avr-gcc :-)
>
> Das ging aber schnell. Ich hoffe, das Feature wurde nicht extra wegen
> meines Beitrags von oben eingebaut ;-)
>
> In welcher Version? Noch in 4.6 oder erst in 4.7?

In 4.7. Für ältere Versionen gibt's bestenfalls Bugfixes, aber teilweise 
noch nichtmal diese (da unerwünscht).

Die Optimierung von Tail Calls ist ein neues Feature, und das kann nur 
in der neuesten Version hinzugefügt werden. Die Optimierung geht über 
das Peepholing der binutils hinaus.

>> Mit dem -fno-split-wide-types sieht's leider nicht so gut aus.
>
> Das ist auch nicht so schlimm. Wichtig ist es zu wissen, dass es da eine
> Option gibt, bei der das Herumspielen u.U. lohnt. Die Defaulteinstellung
> ist dann eher zweitrangig.

Naja, der Feld-Wald-und-Wiesen-Anwender hat bestimmt keine Lust und 
keine Zeit, aus den hunderten von gcc-Optionen die beste Kombination 
herauszuarbeiten.

Neben dieser Option sind mir z.B. auch -fno-move-loop-invariants und 
-fno-tree-loop-optimize unangenehm aufgefallen. Invarianten, die in 
einer Schleife stehen, werden vor die Schleife gezogen. Das erhöht 
teilweise die Registerlast derart, daß ein Stackframe notwendig wird. 
Oder der Wert landet zumindest in einem call-saved Register (vor allem, 
wenn in der Schleife eine Funktion aufgerufebn wird, die eine Konstante 
bekommt), was pushs/pops nach sich zieht oder umständliche 
Befehlssequenzen, wenn eine Konstante in ein unteres Register 0..15 
geschrieben werden muss.

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.