Momentan wundere ich mich über GCC in zwei Punkten: 1) habe ich eine Rechnung in einer Schleife von double auf int umgestellt. Dabei erhöht sich die Rechenzeit von 220 auf 590 mSec. Eigentlich hätte ich eine Beschleunigung erwartet. int, uint32_t und uint_fast_t sind praktisch gleich. Gibt es für die Verlangsamung eine plausible Erklärung oder lohnt es sich da noch mal genauer nachzuschauen ? GCC compiliert -o3 2) bei int = double gibt es beim Compilieren keine Typwarnung. Kann man die überhaupt abstellen oder muß da was oberfaul sein ?
Gib doch mal - die Zielhardware - die 2...3 Programmversionen - die Compileroptionen, insbesondere die zu Optimierung, aber auch Bibliotheken - die Art der Zeitmessung an. Ansonsten lautet die Antwort : 42
Wie fop schon gesagt hat: Viel zu wenig Informationen. Normalerweise sollte man davon ausgehen, dass die Integerrechnung schneller vonstatten geht als die mit double's, aber vielleicht muss dafür, an anderer Stelle, ständig von integer auf double konvertiert werden.
Amateur schrieb: > Normalerweise sollte man davon ausgehen, dass die Integerrechnung > schneller vonstatten geht als die mit double's Der Thread steht in "PC Programmierung" und bei x86 aus diesem Jahrtausend gilt diese Regel nicht. Da ist Fliesskommaverarbeitung sehr schnell und z.B. bei AMD getrennt von der Integerverarbeitung. Der Teufel kann allerdings im Detail stecken, weshalb man dafür minimalisierte aber funktionsfähige Testprogramme benötigt.
:
Bearbeitet durch User
Ok, es ist ein Ubuntu 18.04 LTS auf einer HP Z440 Workstation mit 8 Kernen. (deshalb auch unter PC Programmierung gepostet). GCC ist mit 7.4.0 schon etwas altbacken ist halt bei der LTS dabei und compiliert als Release mit -o3. Die Zeitmessung halte ich für seriös und mache ich mit dem Systemtimer wobei uS wegfallen und mS auf plusminus 1% reproduzierbar sind
1 | auto t2 = std::chrono::high_resolution_clock::now(); |
2 | auto duration = (std::chrono::duration_cast<std::chrono::microseconds>( t2 - t1 ).count())/1000; |
Die Rechnung ist eine Parallelschaltung mit vielen Iterationen:
1 | tmp_result = |
2 | ( int_lut[j] * int_lut[i] ) / |
3 | ( int_lut[j] + int_lut[i] ); |
4 | tmp_result -= intTargetR; |
5 | if( abs( tmp_result ) < (abs( best4R ) ) |
Die 220mS double Version sieht so aus
1 | tmp_result = |
2 | ( comb_lut[j].e_value * comb_lut[i].e_value ) / |
3 | ( comb_lut[j].e_value + comb_lut[i].e_value ); |
4 | tmp_result -= TargetR; |
5 | |
6 | if( abs( tmp_result ) < (abs( best4R ) ) ) |
Der if Zweig trifft selten zu und trägt damit nicht zur Laufzeit bei. Interessanterweise wird die Laufzeit bei double ähnlich schlecht wenn ich die 1/R=1/Ra+1/Rb Formel nehme.
:
Bearbeitet durch User
daneben hätte ich erwartet, dass ein Zugriff auf array of struct länger dauert als auf ein int_array aber es ist genau gleich was für einen guten Optimizer spricht.
1 | struct r_data { |
2 | bool e_use; |
3 | std::string e_name; |
4 | double e_value; |
5 | };
|
6 | |
7 | std::array<r_data,MAX_COMB> comb_lut; |
8 | std::array<int_fast32_t, MAX_COMB> int_lut; |
Aber die Anzahl der Iterationen ist gleich? Ich nehm mal an du hast mit 220ms/590ms nicht nur einen Schleifendurchgang gemessen. Feste Anzahl an Iterationen oder gibt es eine Abbruchbedingung die bedingt durch den Datentp früher oder später abbricht?
das ganze 8 mal:
>cat /proc/cpuinfo
processor : 0
vendor_id : GenuineIntel
cpu family : 6
model : 63
model name : Intel(R) Xeon(R) CPU E5-1620 v3 @ 3.50GHz
stepping : 2
microcode : 0x43
cpu MHz : 1197.228
cache size : 10240 KB
physical id : 0
siblings : 8
core id : 0
cpu cores : 4
apicid : 0
initial apicid : 0
fpu : yes
fpu_exception : yes
cpuid level : 15
wp : yes
flags : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov
pat pse36 clflush dts acpi mmx fxsr sse sse2 ss ht tm pbe syscall nx
pdpe1gb rdtscp lm constant_tsc arch_perfmon pebs bts rep_good nopl
xtopology nonstop_tsc cpuid aperfmperf pni pclmulqdq dtes64 monitor
ds_cpl vmx smx est tm2 ssse3 sdbg fma cx16 xtpr pdcm pcid dca sse4_1
sse4_2 x2apic movbe popcnt tsc_deadline_timer aes xsave avx f16c rdrand
lahf_lm abm cpuid_fault epb invpcid_single pti intel_ppin ssbd ibrs ibpb
stibp tpr_shadow vnmi flexpriority ept vpid ept_ad fsgsbase tsc_adjust
bmi1 avx2 smep bmi2 erms invpcid cqm xsaveopt cqm_llc cqm_occup_llc
dtherm ida arat pln pts md_clear flush_l1d
bugs : cpu_meltdown spectre_v1 spectre_v2 spec_store_bypass l1tf mds
swapgs itlb_multihit
bogomips : 6983.82
clflush size : 64
cache_alignment : 64
address sizes : 46 bits physical, 48 bits virtual
power management:
processor : 1
vendor_id : GenuineIntel
cpu family : 6
model : 63
model name : Intel(R) Xeon(R) CPU E5-1620 v3 @ 3.50GHz
stepping : 2
microcode : 0x43
cpu MHz : 1197.264
cache size : 10240 KB
physical id : 0
siblings : 8
core id : 1
cpu cores : 4
apicid : 2
initial apicid : 2
fpu : yes
fpu_exception : yes
cpuid level : 15
wp : yes
flags : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov
pat pse36 clflush dts acpi mmx fxsr sse sse2 ss ht tm pbe syscall nx
pdpe1gb rdtscp lm constant_tsc arch_perfmon pebs bts rep_good nopl
xtopology nonstop_tsc cpuid aperfmperf pni pclmulqdq dtes64 monitor
ds_cpl vmx smx est tm2 ssse3 sdbg fma cx16 xtpr pdcm pcid dca sse4_1
sse4_2 x2apic movbe popcnt tsc_deadline_timer aes xsave avx f16c rdrand
lahf_lm abm cpuid_fault epb invpcid_single pti intel_ppin ssbd ibrs ibpb
stibp tpr_shadow vnmi flexpriority ept vpid ept_ad fsgsbase tsc_adjust
bmi1 avx2 smep bmi2 erms invpcid cqm xsaveopt cqm_llc cqm_occup_llc
dtherm ida arat pln pts md_clear flush_l1d
bugs : cpu_meltdown spectre_v1 spectre_v2 spec_store_bypass l1tf mds
swapgs itlb_multihit
bogomips : 6983.82
clflush size : 64
cache_alignment : 64
address sizes : 46 bits physical, 48 bits virtual
Fliesskomma Rechnung ist in einer FPU schon sehr schnell, aber eben keine Integergeschwindigkeit. Da fehlt schon noch ein Stueck. Vielleicht nicht ganz unwichtig... Eine gleich grosse Integer Zahl hat eine hoehere Genauigkeit wie eine Floatzahl.
:
Bearbeitet durch User
es sind in beiden Fälle etwa 26 Mio Iterationen mit einer herkömmlichen verschachtelten for Śchleife (index i,j), keine range based loop.
Was macht der denn da für Instruktionen draus? Vielleicht wird der double Code irgendwie autovektorisiert oder sowas. Hier kann man sowas austesten: https://godbolt.org/
Danke für das hübsche Spielzeug. Damit brauch ich nicht an cmake rumbasteln. Ich muss mir noch etwas Zeit nehmen das ganze weiter zu isolieren. Wenn ich zwischen den Zeilen lese sollte sich eine nähere Betrachtung doch lohnen da auch andere hier erwarten daß int schneller als double sein sollte.
J. V. schrieb: > Danke für das hübsche Spielzeug. Ganz ehrlich: Wer Mikro-Optimierungen betrieben will, und über cache-misses schwadroniert, sollte in der Lage sein, seiner Toolchain Disassemblies zu entlocken, und die auch zu verstehen. Oliver
macht gcc/cc von sich aus Gebrauch von AVX/SSEx ? Ich würde gern mal das Compilat des Schnipsels sehen von beiden Versionen. So ein Gedanke: Fliesskomma Richtung SSE auslagern und Integer auf dem Standardbefehlssatz.
Dennis H. schrieb: > macht gcc/cc von sich aus Gebrauch von AVX/SSEx ? Muss er halt nachschauen, wie sein gcc configuriert ist. Üblicherweise erstellt der generischen (ARM64)-Code. Wer aber auf dem level optimiert, soellte -march=native und vielleicht auch noh ein paar weitere Optionen nutzen. Oliver
J.V.: Haste schonmal auf so einer Seite geschaut ? https://docs.microsoft.com/de-de/cpp/intrinsics/x64-amd64-intrinsics-list?view=vs-2019 Da kann man noch einiges Rauskitzeln mit direkten SSE-Befehlen in C. Erfreulicherweise gibt es dann Datentypen die das Handhaben vereinfachen. In der Liste fehlen jetzt leider die AVX512-Befehle. Ich habe das mal mit Anwendung auf ein Numpy-Array genutzt. Das Numpy-Eigene OR war mir zu langsam. Mit __m256i* pnt=PyArray_DATA(in_array); __m256i q0,q1,q2,q3; uint64_t t=PyArray_SIZE(in_array)/16/4/2; while(t--) { q0=*pnt; q1=*(pnt+1); q2=*(pnt+2); q3=*(pnt+3); *pnt++=_mm256_or_si256(q0,p); *pnt++=_mm256_or_si256(q1,p); *pnt++=_mm256_or_si256(q2,p); *pnt++=_mm256_or_si256(q3,p); } gings direkt 4x so schnell.
Oliver S. schrieb: > Wer aber auf dem level optimiert, soellte -march=native und vielleicht > auch noh ein paar weitere Optionen nutzen. Oliver S. schrieb: > Ganz ehrlich: Wer Mikro-Optimierungen betrieben will, und über > cache-misses schwadroniert, Halt mal etwas den Ball flach. Ich habe eher das Gefühl der TE interessiert sich einfach dafür was da genau los ist. Er ist zufällig über eine Merkwürdigkeit gestolpert und will das nun genauer untersuchen. Und auch die Mitleser (inklusive mir) scheinen das interessant zu finden weil das beobachtete Verhalten erstmal ungewöhnlich scheint. Von einem zwingenden (Mikro-) Optimierungsbedarf lese ich da nichts.
:
Bearbeitet durch User
Mein erster Verdächtiger wäre die Division. Übersetze das Programm mal mit den Optionen -g -S und schau dir mal den Assembler Output an. Durch die -g Option ist im Assemblertext auch die ursprüngliche Zeilennummer enthalten, wonach du suchen kannst.
Joggel E. schrieb: > Fliesskomma Rechnung ist in einer FPU schon sehr schnell, aber eben > keine Integergeschwindigkeit. Die Fliesskomma-Division ist heute wesentlich schneller als die Integer-Division (seit Intel Core 2 und AMD K7). CPU ist Haswell, dafür gilt lt. Agner Fog als Latenz: - Integer Division u32: 22-29 Takte - SSE Fliesskomma 32/64: 10-13 Takte Beim Durchsatz sind es 9-11 vs 7 Takte. Das ist der Wert, nach dem die nächste unabhängige Division gestartet werden kann.
:
Bearbeitet durch User
CPU ist Haswell, dafür gilt lt. Agner Fog als Latenz: - Integer Division u32: 22-29 Takte - SSE Fliesskomma 32/64: 10-13 Takte Knapp Faktor 3 könnte sogar passen. Die Laufzeit verlängert sich von 220mSec auf 580 mSec und das ist nicht von Pappe und hat wohl auch nix mit Mikrooptimierung zu tun. (Ein Mix-Listing habe ich leider noch immer nicht gekriegt)
J. V. schrieb: > Ok, es ist ein Ubuntu 18.04 LTS auf einer HP Z440 Workstation mit 8 > Kernen. > (deshalb auch unter PC Programmierung gepostet). GCC ist mit 7.4.0 schon > etwas altbacken ist halt bei der LTS dabei Mach mal das Ubuntu nicht schlechter als es ist: gcc in Version 8.3 ist auch bei der 18.04 LTS als Paket zu haben.
hier mal die Integer Version
1 | 402:/home/jv/kicad/pcb_calculator/eseries.cpp **** tmp_result = |
2 | 403:/home/jv/kicad/pcb_calculator/eseries.cpp **** ( int_lut[j] * int_lut[i] ) / |
3 | 5980 .loc 17 403 0 |
4 | 5981 09c0 480FAFC3 imulq %rbx, %rax |
5 | 402:/home/jv/kicad/pcb_calculator/eseries.cpp **** ( int_lut[j] * int_lut[i] ) / |
6 | 5982 .loc 17 402 0 |
7 | 5983 09c4 4899 cqto |
8 | 5984 09c6 48F7FF idivq %rdi |
9 | 5985 .LVL555: |
10 | 404:/home/jv/kicad/pcb_calculator/eseries.cpp **** ( int_lut[j] + int_lut[i] ); // calculate 2R|2R parallel |
11 | 405:/home/jv/kicad/pcb_calculator/eseries.cpp **** tmp_result -= intTargetR; // calculate 4R deviation |
12 | 5986 .loc 17 405 0 |
13 | 5987 09c9 482B4424 subq 8(%rsp), %rax |
14 | 5987 08 |
15 | 5988 .LVL556: |
16 | 5989 .LBB7424: |
17 | 5990 .LBB7425: |
18 | 5991 .LBB7426: |
19 | 56:/usr/include/c++/7/bits/std_abs.h **** #endif |
20 | 5992 .loc 32 56 0 |
21 | 5993 09ce 4889C2 movq %rax, %rdx |
22 | 5994 .LBE7426: |
23 | 5995 .LBE7425: |
24 | 5996 .LBE7424: |
25 | 5997 .loc 17 405 0 |
26 | 5998 09d1 4889C3 movq %rax, %rbx |
27 | 5999 .LVL557: |
28 | 6000 .LBB7567: |
29 | 6001 .LBB7428: |
30 | 6002 .LBB7427: |
31 | 56:/usr/include/c++/7/bits/std_abs.h **** #endif |
32 | 6003 .loc 32 56 0 |
33 | 6004 09d4 48C1FA3F sarq $63, %rdx |
34 | 6005 09d8 4889D0 movq %rdx, %rax |
35 | 6006 .LVL558: |
36 | 6007 09db 4831D8 xorq %rbx, %rax |
37 | 6008 09de 4829D0 subq %rdx, %rax |
38 | 6009 .LBE7427: |
39 | 6010 .LBE7428: |
40 | 406:/home/jv/kicad/pcb_calculator/eseries.cpp **** |
41 | 407:/home/jv/kicad/pcb_calculator/eseries.cpp **** if( abs( tmp_result ) < (abs( best4R ) ) ) // if new 4R is better |
42 | 6011 .loc 17 407 0 |
43 | 6012 09e1 4839C8 cmpq %rcx, %rax |
44 | 6013 09e4 0F8CE601 jl .L492 |
45 | 6013 0000 |
46 | 6014 .LVL559: |
47 | 6015 09ea 4983C508 addq $8, %r13 |
48 | 6016 09ee 4883C530 addq $48, %rbp |
49 | 6017 .LBE7567: |
50 | 6018 .LBE7579: |
51 | 382:/home/jv/kicad/pcb_calculator/eseries.cpp **** { |
52 | 6019 .loc 17 382 0 discriminator 2 |
53 | 6020 09f2 48396C24 cmpq %rbp, 40(%rsp) |
54 | 6020 28 |
55 | 6021 09f7 0F845903 je .L493 |
56 | 6021 0000 |
und hier die float Version
1 | 402:/home/jv/kicad/pcb_calculator/eseries.cpp **** tmp_result = |
2 | 403:/home/jv/kicad/pcb_calculator/eseries.cpp **** ( int_lut[j] * int_lut[i] ) / |
3 | 5980 .loc 17 403 0 |
4 | 5981 09c0 480FAFC3 imulq %rbx, %rax |
5 | 402:/home/jv/kicad/pcb_calculator/eseries.cpp **** ( int_lut[j] * int_lut[i] ) / |
6 | 5982 .loc 17 402 0 |
7 | 5983 09c4 4899 cqto |
8 | 5984 09c6 48F7FF idivq %rdi |
9 | 5985 .LVL555: |
10 | 404:/home/jv/kicad/pcb_calculator/eseries.cpp **** ( int_lut[j] + int_lut[i] ); // calculate 2R|2R parallel |
11 | 405:/home/jv/kicad/pcb_calculator/eseries.cpp **** tmp_result -= intTargetR; // calculate 4R deviation |
12 | 5986 .loc 17 405 0 |
13 | 5987 09c9 482B4424 subq 8(%rsp), %rax |
14 | 5987 08 |
15 | 5988 .LVL556: |
16 | 5989 .LBB7424: |
17 | 5990 .LBB7425: |
18 | 5991 .LBB7426: |
19 | 56:/usr/include/c++/7/bits/std_abs.h **** #endif |
20 | 5992 .loc 32 56 0 |
21 | 5993 09ce 4889C2 movq %rax, %rdx |
22 | 5994 .LBE7426: |
23 | 5995 .LBE7425: |
24 | 5996 .LBE7424: |
25 | 5997 .loc 17 405 0 |
26 | 5998 09d1 4889C3 movq %rax, %rbx |
27 | 5999 .LVL557: |
28 | 6000 .LBB7567: |
29 | 6001 .LBB7428: |
30 | 6002 .LBB7427: |
31 | 56:/usr/include/c++/7/bits/std_abs.h **** #endif |
32 | 6003 .loc 32 56 0 |
33 | 6004 09d4 48C1FA3F sarq $63, %rdx |
34 | 6005 09d8 4889D0 movq %rdx, %rax |
35 | 6006 .LVL558: |
36 | 6007 09db 4831D8 xorq %rbx, %rax |
37 | 6008 09de 4829D0 subq %rdx, %rax |
38 | 6009 .LBE7427: |
39 | 6010 .LBE7428: |
40 | 406:/home/jv/kicad/pcb_calculator/eseries.cpp **** |
41 | 407:/home/jv/kicad/pcb_calculator/eseries.cpp **** if( abs( tmp_result ) < (abs( best4R ) ) ) // if new 4R is better |
42 | 6011 .loc 17 407 0 |
43 | 6012 09e1 4839C8 cmpq %rcx, %rax |
44 | 6013 09e4 0F8CE601 jl .L492 |
45 | 6013 0000 |
46 | 6014 .LVL559: |
47 | 6015 09ea 4983C508 addq $8, %r13 |
48 | 6016 09ee 4883C530 addq $48, %rbp |
49 | 6017 .LBE7567: |
50 | 6018 .LBE7579: |
51 | 382:/home/jv/kicad/pcb_calculator/eseries.cpp **** { |
52 | 6019 .loc 17 382 0 discriminator 2 |
53 | 6020 09f2 48396C24 cmpq %rbp, 40(%rsp) |
54 | 6020 28 |
55 | 6021 09f7 0F845903 je .L493 |
Float ist beim Kopieren über die Zwischenablage falsch gelaufen Hier also nochmal [c] 399:/home/jv/kicad/pcb_calculator/eseries.cpp **** tmp_result = 400:/home/jv/kicad/pcb_calculator/eseries.cpp **** ( comb_lut[j].e_value * comb_lut[i].e_value ) / 6235 .loc 17 400 0 6236 0a40 F20F59C2 mulsd %xmm2, %xmm0 399:/home/jv/kicad/pcb_calculator/eseries.cpp **** ( comb_lut[j].e_value * comb_lut[i].e_value ) / 6237 .loc 17 399 0 6238 0a44 F20F5EC4 divsd %xmm4, %xmm0 6239 .LVL584: 401:/home/jv/kicad/pcb_calculator/eseries.cpp **** ( comb_lut[j].e_value + comb_lut[i].e_value ); // calculate 2R|2R parallel 402:/home/jv/kicad/pcb_calculator/eseries.cpp **** tmp_result -= targetR; // calculate 4R deviation 6240 .loc 17 402 0 6241 0a48 F20F5CC3 subsd %xmm3, %xmm0 6242 .LVL585: 6243 .LBB8220: 6244 .LBB8221: 6245 .LBB8222: 6246 .loc 32 71 0 6247 0a4c 660F28D0 movapd %xmm0, %xmm2 6248 0a50 660F5415 andpd .LC9(%rip), %xmm2 6248 00000000 6249 .LBE8222: 6250 .LBE8221: 403:/home/jv/kicad/pcb_calculator/eseries.cpp **** 404:/home/jv/kicad/pcb_calculator/eseries.cpp **** if( abs( tmp_result ) < abs( rslt_lut[S4R].e_value ) ) // if new 4R is better 6251 .loc 17 404 0 6252 0a58 660F2ECA ucomisd %xmm2, %xmm1 6253 0a5c 0F87DE01 ja .L513 6253 0000 6254 .LVL586: 6255 .L471: 6256 0a62 4883C330 addq $48, %rbx 6257 .LBE8220: 6258 .LBE8365: 380:/home/jv/kicad/pcb_calculator/eseries.cpp **** { 6259 .loc 17 380 0 discriminator 2 6260 0a66 483B5C24 cmpq 8(%rsp), %rbx 6260 08 6261 0a6b 0F843F03 je .L514 6261 0000 6262 .L479: 6263 .LBB8366: 382:/home/jv/kicad/pcb_calculator/eseries.cpp **** tmp_result -= targetR; // calculate 4R deviation 6264 .loc 17 382 0 6265 0a71 F20F1053 movsd 32(%rbx), %xmm2
Kann leider nicht mehr editieren da ich über timeout ausgeloggt wurde. Die Codeunterschiede sehen gar nicht soo arg aus alsda wären mulsd und divsd für die double Version und imulq und idivq für die interger Version
Das ist die 64-Bit Integer Version mit Vorzeichen. Die ist noch langsamer. Latenz: - Integer Division s64: 39-103 Takte - SSE Fliesskomma 32/64: 10-13 Takte Durchsatz: - Integer Division s64: 24-81 Takte - SSE Fliesskomma 32/64: 7 Takte Gegenüber dem Haswell ist bei Integers sogar der Goldmont (Atom) in meinem Netbook schneller (s64: 13-43, double: 34). Also Leute, von der Idee das bei den grossen x86 Integers schneller als Doubles seien, bitte ganz schnell abschwören. Nur bei den kleinen stimmts. ;-)
:
Bearbeitet durch User
J. V. schrieb: > daneben hätte ich erwartet, dass ein Zugriff auf array of struct > länger > dauert als auf ein int_array aber es ist genau gleich was für einen > guten Optimizer spricht. > struct r_data { > bool e_use; > std::string e_name; > double e_value; > }; > > std::array<r_data,MAX_COMB> comb_lut; > std::array<int_fast32_t, MAX_COMB> int_lut; Keine Ahnung wo dein Wissen her kommt - nach Gefühl fühlt es sich an als wuerdest du 286/386 Erfahrungen auf aktuelle Hardware spiegeln, die meisten deiner Vermutungen (auch in anderen Posts) sind schlicht und einfach falsch, sorry Deine Zeitmessung ist viel zu ungenau um diesen Unterschied zu erkennen, selbst wenn es einen geben würde
Schau doch einfach auf den assemblercode, der Optimizer hatte da nur 1982 probleme mit - deswegen finde ich deine Aussagen so verwirrent
> Keine Ahnung wo dein Wissen her kommt
Habs ja eingesehen. Beim Anschauen von /proc/cpuinfo ist klar geworden
daß bei den Cachegrößen sowieso die gesamte LUT reinpasst. Egal ob mit
oder ohne struct. Ebenso habe ich mich halt auch mit den floats
verpeilt. Auch wenn ich hier die Hocke vollkriege, war ich zumindest
nicht der Einzige der danebengeschätzt hat.
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.