Matroids Matheplanet Forum Index
Moderiert von matroid
Informatik » Programmieren » Header einer C-Routine
Thema eröffnet 2023-08-15 15:28 von juergenX
Seite 2   [1 2]   2 Seiten
Autor
Universität/Hochschule J Header einer C-Routine
zippy
Senior Letzter Besuch: im letzten Monat
Dabei seit: 24.10.2018
Mitteilungen: 5147
  Beitrag No.40, eingetragen 2023-08-20

Nur am Rande: Wozu soll das gut sein? \quoteon(2023-08-20 23:22 - hyperG in Beitrag No. 38) g++ -std=c++0x -m64 -march=native -Ofast -Wall -Wextra -Wpedantic -O3 -s mul256inclAusgabe.cpp \quoteoff Die O3-Option setzt doch die weitergehende Ofast-Option wieder außer Kraft. (Zitat Doku: "If you use multiple ‘-O’ options, with or without level numbers, the last such option is the one that is effective.") --zippy [Die Antwort wurde nach Beitrag No.38 begonnen.]


   Profil
hyperG
Senior Letzter Besuch: in der letzten Woche
Dabei seit: 03.02.2017
Mitteilungen: 2159
  Beitrag No.41, eingetragen 2023-08-21

\quoteon(2023-08-20 22:54 - polygamma in Beitrag No. 36) ... Was meinst du damit? Schneller als was? ... \quoteoff na dass Dein Algorithmus "ohne Wandlung von String nach Zieltyp": mpz_mul + mpz_add_ui Schneller als mein Algorithmus "mit Konvertierung von String zum Zieltyp": sprintf + mpz_set_str + mpz_mul ist (egal ob nun mit der Sprache GMP Zieltyp mpz oder Linux c mit Hilfe __uint128_t zum Zieltyp "struct u256b"). Noch deutlicher wird es, wenn man eine Quelldatei mit 1 Mrd Strings hat, und 1 Mrd. Ergebnisse in einer anderen Datei herauskommen soll. (da kommt dann noch gmp_printf dazu) Ist aber schon interessant, dass der reine u256b_multiply mit gutem Linux-Compiler etwa 10 mal schneller als mpz_mul ist. [Die Antwort wurde nach Beitrag No.38 begonnen.]


   Profil
hyperG
Senior Letzter Besuch: in der letzten Woche
Dabei seit: 03.02.2017
Mitteilungen: 2159
  Beitrag No.42, eingetragen 2023-08-21

\quoteon(2023-08-20 23:36 - zippy in Beitrag No. 40) Nur am Rande: Wozu soll das gut sein? \quoteon(2023-08-20 23:22 - hyperG in Beitrag No. 38) g++ -std=c++0x -m64 -march=native -Ofast -Wall -Wextra -Wpedantic -O3 -s mul256inclAusgabe.cpp \quoteoff Die O3-Option setzt doch die weitergehende Ofast-Option wieder außer Kraft. (Zitat Doku: "If you use multiple ‘-O’ options, with or without level numbers, the last such option is the one that is effective.") --zippy [Die Antwort wurde nach Beitrag No.38 begonnen.] \quoteoff d:\mingw64\bin\g++ -std=c++0x -m64 -march=native -Ofast -Wall -Wextra -Wpedantic -s mul256inclAusgabe.cpp -o mul256inclAusgabeNarr.exe Erzeugt zwar eine exe, aber die stürzt nach dem Start sofort ab. d:\mingw64\bin\gcc -Ofast -s mul256inclAusgabe.cpp -o mul256inclAusgabe.exe ändert kaum was: 32 s Alles noch meilenweit entfernt vom Online Compiler mit etwa 12 s


   Profil
Ixx
Aktiv Letzter Besuch: in der letzten Woche
Dabei seit: 05.04.2020
Mitteilungen: 382
  Beitrag No.43, eingetragen 2023-08-21

\quoteon(2023-08-21 00:09 - hyperG in Beitrag No. 41) Ist aber schon interessant, dass der reine u256b_multiply mit gutem Linux-Compiler etwa 10 mal schneller als mpz_mul ist. \quoteoff Eigentlich nicht. Dass ein Algorithmus, der für einen Spezialfall eines Problems optimiert wurde, bei diesem Spezialfall schneller ist als einer, der ein viel allgemeineres Problem löst, sollte jetzt nicht wirklich verwundern. BTW: Wenn ich das richtig verstanden habe, dann will Jürgen ein paar Collatz-Iterationen berechnen. Entweder, das ist eine Spielerei — dann tut es auch eine naive Implementation eines 256-Bit-Datentyps — oder, es sollen *sehr* viele Startzahlen betrachtet werden. Aber auch dann ist eher eine schnelle 128-Bit-Arithmetik notwendig und auch hier reicht eine rudimentäre 256-Bit-Betrachtung aus, da der deutlich, deutlich, deutlich überwiegende Anteil der Rechnungen sich unterhalb von 2^128 abspielt. Hier optimiert man m.E. nicht an der geeigneten Stelle. (Und ich sage das, weil ich selbst mal wieder seit Monaten immer mal weiter an meinem Collatz-Projekt arbeite, um David Barina mit seiner Implementation ( https://pcbarina.fit.vutbr.cz/ ) zu schlagen. Aber der macht sehr viel richtig; und meine algorithmischen Ideen wollen auch sauber umgesetzt werden…) [Die Antwort wurde vor Beitrag No.42 begonnen.]


   Profil
zippy
Senior Letzter Besuch: im letzten Monat
Dabei seit: 24.10.2018
Mitteilungen: 5147
  Beitrag No.44, eingetragen 2023-08-21

\quoteon(2023-08-21 00:21 - hyperG in Beitrag No. 42) Alles noch meilenweit entfernt vom Online Compiler mit etwa 12 s \quoteoff Es ging mir nicht um die Geschwindigkeit, sondern nur darum, dass ein gcc-Aufruf, der beide Optionen enthält, keinen Sinn ergibt.


   Profil
juergenX
Aktiv Letzter Besuch: in der letzten Woche
Dabei seit: 08.07.2019
Mitteilungen: 942
  Beitrag No.45, vom Themenstarter, eingetragen 2023-08-23

Erstmal vielen Dank für den gewaltigen Response! Die Routine quasi Karatsuba procedure dürfte auf einem guten Intel wohl die besten Resultate ergeben. Ganz ohne C bekommenn wir das Problem der richtigen Initialisierung. Wir hätten als Vorgabe zu berechnen 156327963765257101755948112982892178397 * 656327963765257101755948112982892178393 Ergebnis ist wie aus Beitrag #39 zu ersehen : 102602414137620088241174671551465753558236494655099839643647545871860456424623 Ich denke mal das stimmt;) Mich interessiert wie übergeben wie die 2* 4 * 64 Bit Blöcke an die reine Assembler Routine wie in #39? rsi und rdi und rdx sind wohl Zeiger auf Zwischenspeicher rax und r8, r9? Und wenn ich den BP dann um 0x8 kleiner mache, zeigen die auf den andere Faktor? Dann mov 0x18(%rdx), %rcx verstehe ich schon nicht, ist das der Zeiger uuf das Ergebnis, der kommt nach rcx? Weiter zu folgen gelingt mir nicht so recht.. Vor allem müssen ja 156327963765257101755948112982892178397 und 656327963765257101755948112982892178393 erstmal in je 4 Blocke gewandelt werden. so dass wir 8 Register 64 bit als 2 Faktoren haben. Also die Doku des progs mul256brute ist schlecht bzw. nicht vorhanden. Und wie die Wandlung Dec to Hec und der Aufruf in Assembler (masm)? ml256brute ( usb256 *x1 , usb256 *x2) Evtl. über die binary to Dec Methode weiter oben?! Und am Ende alles Rückwärts. Als Stack Pointer Referenz wird immer rbp genommen, siehe mov -0x10(%rsp), %r12 Am Ende alles Rückwärts, Wo genaus steht das 4 Block Ergebnis? Und es muss ja noch in dec gewandelt werde, wir wissen ja jetzt wie.(#33) \sourceon ASM mul256brute: mov (%rsi), %rax mov (%rdx), %r8 mov 0x8(%rdx), %r9 mov %rbp, -0x8(%rsp) mov %rax, %rbp mov 0x18(%rdx), %rcx imul %rbp, %rcx mov 0x10(%rdx), %r10 mul %r8 mov %rax, (%rdi) mov %rdx, %r11 mov %rbp, %rax mul %r10 mov %r12, -0x10(%rsp) mov %rax, %r12 add %rdx, %rcx mov %rbp, %rax mul %r9 add %rax, %r11 mov 0x8(%rsi), %rbp adc %rdx, %r12 adc $0, %rcx imul %rbp, %r10 mov %rbp, %rax mul %r8 add %rax, %r11 mov %rbp, %rax adc %rdx, %r12 adc %r10, %rcx mov 0x10(%rsi), %rbp mul %r9 mov %r11, 0x8(%rdi) imul %rbp, %r9 add %rax, %r12 mov %rbp, %rax adc %rdx, %rcx mov 0x18(%rsi), %rbp imul %r8, %rbp add %rbp, %rcx mov -0x8(%rsp), %rbp mul %r8 add %rax, %r12 mov %r12, 0x10(%rdi) mov -0x10(%rsp), %r12 adc %r9, %rcx add %rdx, %rcx mov %rcx, 0x18(%rdi) retq \sourceoff Ich erwarte nicht eine vollständige Doku. Aber so rätselt man doch sehr herum. Am liebsten hätte ich ein fertiges in MASM Compilierbares Programm. Multiplikation 2er Dezimal-Zahlen mit Dezimal-Ergebnis. Das kann ich hier aber von keinem verlangen. 😄🤔🙄🤒😁


   Profil
hyperG
Senior Letzter Besuch: in der letzten Woche
Dabei seit: 03.02.2017
Mitteilungen: 2159
  Beitrag No.46, eingetragen 2023-08-24

Hallo juergenX, mit Microsoft® Macro Assembler (x64) habe ich herumgespielt und konnte den Syntax auch anpassen. Natürlich zunächst mit addASM, um die Fehlersuche klein zu halten. Jedoch hat die Microsoft/VC Welt ganz andere Einsprungs-Register, andere Reihenfolge & es gibt zig Arten der Übergabe und Rückgabe... (viel Zeit & kaum Erfolge) Dafür habe ich das hier gefunden mit AVX2 Befehlen: \sourceon cpp __m256i add256AVX(uint32_t& carry, __m256i A, __m256i B) { A = _mm256_xor_si256(A, _mm256_set1_epi64x(0x8000000000000000)); __m256i s = _mm256_add_epi64(A, B); __m256i cv = _mm256_cmpgt_epi64(A, s); __m256i mv = _mm256_cmpeq_epi64(s, _mm256_set1_epi64x(0x7fffffffffffffff)); uint32_t c = _mm256_movemask_pd(_mm256_castsi256_pd(cv)); uint32_t m = _mm256_movemask_pd(_mm256_castsi256_pd(mv)); { c = m + 2 * c; // lea carry += c; m ^= carry; carry >>= 4; m &= 0x0f; } return _mm256_add_epi64(s, BROADCAST_MASK[m]); } /* __m256i _mm256_add_epi64 (__m256i a, __m256i b) macht mit 1 Maschinenbefehl das hier: FOR j := 0 to 3 i := j*64 dst[i+63:i] := a[i+63:i] + b[i+63:i] ENDFOR dst[MAX:256] := 0 */ \sourceoff Mit Struct & Union + Anpassung (fast) aller Funktionen für den bei VC fehlenden Typ __uint128_t hat das superschnell funktioniert. (dürfte sogar schneller als addASM sein... Bei der Multiplikation habe ich noch nichts für mul256AVX gefunden (nur Arrays oder mit Vorzeichen...). Dafür fand ich was, an dem ich noch arbeite. Falls es funktioniert, wäre es ein Quantensprung -> ich melde mich ... Grüße Gerd


   Profil
juergenX
Aktiv Letzter Besuch: in der letzten Woche
Dabei seit: 08.07.2019
Mitteilungen: 942
  Beitrag No.47, vom Themenstarter, eingetragen 2023-08-26

Nochmal danke für die vielen Anregungen! ich hab den richtigen Aufruf meiner ursprünglichen Routine leider noch nicht verstanden.. Diese pointerstruktur ist unklar sry... \sourceon c typedef unsigned long long u64b; typedef __uint128_t u128b; typedef struct u256b u256b; struct u256b { u64b lo; u64b mid; u128b hi; }; mul128b: imul %rdi,%rcx mov %rdx,%rax imul %rdx,%rsi mul %rdi add %rsi,%rcx lea (%rcx,%rdx,1),%rdi mov %rdi,%rdx retq // Der aufruf muesste dann wohl richtig so heissen? x:u256b = (2,1,1)// als Beispiel = 2+2^64+2^128 y:u256b = (3,0,2) // als Beispiel = 3+2*2^128 z= mul128b(*x,*y) \sourceoff Kann das jemand berichtigen? Multiplizieren wir pointer?


   Profil
hyperG
Senior Letzter Besuch: in der letzten Woche
Dabei seit: 03.02.2017
Mitteilungen: 2159
  Beitrag No.48, eingetragen 2023-08-26

Quantensprung hat sich erledigt, da meine CPU zwar AVX512 hat, aber ich viel zu spät bemerkte, dass es noch viele Unterbefehlsgruppen gibt, die meine CPU aber nicht alle unterstützt! (AVX512IFMA fehlt!; viele Abstürze & viel Zeit vergeudet, da es manchmal nicht abstürzte -> und ich an der falschen Stelle suchte) Dann habe ich endlich mul128Bit hinbekommen (was unter VC nicht gibt & beim mingw-w64 viel zu langsam war: 32 s): - ASM -> ml64.exe -> obj -> 73333 mul256inclSprintf dann 17.1 s - AVX für den mul128 Teil -> 73333 mul256inclSprintf dann 17.4 s Immerhin fast eine Verdopplung der Geschwindigkeit (und mit Optimierung der String-Nach-u256 könnte man bis 3 s herunterkommen), aber immer noch meilenweit von GMP entfernt. analog Beitrag No.37 (1.1 s & 9,9 GMP), bei mir: \sourceon nameDerSprache 656327963765257101755948112982892178393 * 156327963765257101755948112982111111111 = 102602414137620088241174671551465753558236494655099839643647545871860456424623 in 9.729 s GMP 656327963765257101755948112982892178393 * 156327963765257101755948112982111111111 = 102602414137620088241174671551465753558236494655099839643647545871860456424623 in 46.441 s u256b_multiplyGL \sourceoff Dann fand ich zwar eine 1024 Bit mul mit AVX512: https://github.com/vkrasnov/vpmadd/blob/master/avx512_mul1024.s aber leider nur in ASM Syntax der Linux-Welt und mein gcc für win10 meckert nach g++ -c avx512mul1024.s -o avx512mul1024.o in der Zeile \sourceon asm .type mul1024_avx512, @function ----> Error 1 ; ... hier code der Funktion vmovdqu64 ACC3, 64*3(res) ret .size mul1024_avx512, .-mul1024_avx512 --->Error 2 ;avx512_mul1024.s:177: Warning: .type pseudo-op used outside of .def/.endef: ignored. ;avx512_mul1024.s:177: Error: junk at end of line, first unrecognized character is `m' ;avx512_mul1024.s:630: Warning: .size pseudo-op used outside of .def/.endef: ignored. ;avx512_mul1024.s:630: Error: junk at end of line, first unrecognized character is `m' \sourceoff Schade... [Die Antwort wurde nach Beitrag No.46 begonnen.]


   Profil
hyperG
Senior Letzter Besuch: in der letzten Woche
Dabei seit: 03.02.2017
Mitteilungen: 2159
  Beitrag No.49, eingetragen 2023-08-26

\quoteon(2023-08-26 19:34 - juergenX in Beitrag No. 47) ... Kann das jemand berichtigen? Multiplizieren wir pointer? \quoteoff Nein, rcx und rax sind normale 64 Bit Register, die bei Deinem Beispiel sofort an imul 1:1 übergeben werden. Allerdings hast Du viele Welten vermischt: a) Linux-Welt um gcc Compiler: - entweder den internen \sourceon c uint16_t MeineVariable; asm volatile ("imul %h0, %b0" : "+a" (MeineVariable) ); \sourceoff siehe https://gcc.gnu.org/onlinedocs/gcc/Extended-Asm.html#Output-Operands oder https://www.felixcloutier.com/documents/gcc-asm.html hier zum Spielen: https://godbolt.org/z/2LlfoL -oder den externen mit Umweg der .o Datei. (so wie bei Deinem Beispiel, jedoch mehr drumherum, wo ich mich aber nicht auskenne). Wie hier die Variablen an die Register weitergeleitet werden, ist mir auch nicht bekannt! Sind aber meist andere als in der Windows Welt! (Schlagwort "Aufrufkonventionen") Mein Mingw emuliert den gcc unter Windows, bringt aber oft Fehler und verhält sich anders als das Original unter Linux. b) Windows Welt z.B. VC: - Bei 64 Bit gibt es den internen ASM-Compiler nicht mehr. (beim teuren Intel-Compiler mag das noch gehen) - Also Extern (das geht immer, allerdings anderer Syntax!): \sourceon asm mulASM128 proc mov rax, rcx mul rdx ; Put the low digit in place (hi is already there) mov qword ptr [r8], rdx ; hier wird der Hi Teil auf den vorhandenen Ptr gelegt ret ; Return rax = Lo-Anteil mulASM128 endp \sourceoff Dann ml64.exe -> obj -> diese dann in das cpp Projekt einbinden und mit \sourceon cpp extern "C" unsigned __int64 mulASM128(unsigned __int64 a, unsigned __int64 b, unsigned __int64 *mulHi); //da die Register getrennt kommen, kann man sie z.B. so in eine Struktur schieben (Profis können das schon in ASM, aber kompliziert): u128b Mul64x64_128(unsigned __int64 a, unsigned __int64 b) { u128b u128ret;//Strukturtyp, den man sich definieren muss u128ret.lo = mulASM128(a,b, &u128ret.hi); return u128ret; } //Aufruf: u128b Meine128BitVariable=Mul64x64_128(2^63, 2^62); \sourceoff


   Profil
hyperG
Senior Letzter Besuch: in der letzten Woche
Dabei seit: 03.02.2017
Mitteilungen: 2159
  Beitrag No.50, eingetragen 2023-08-26

Verwirrend: während gcc aus \sourceon c unsigned long long mul128(unsigned long long a, unsigned long long b, unsigned long long *c) { *c=7; return a*b; } \sourceoff das hier macht: \sourceon asm ;mul128(unsigned long long, unsigned long long, unsigned long long*): mov rax, rdi mov QWORD PTR [rdx], 7 imul rax, rsi ret \sourceoff also die Übergabeparameter in rdi, rsi und rdx kommen, Sind das bei VC: RCX, RDX , r8 Ptr-Syntax ist normalerweise: QWORD PTR [rdx + 7h] bei gcc extern: 0x7(%rdx)


   Profil
zippy
Senior Letzter Besuch: im letzten Monat
Dabei seit: 24.10.2018
Mitteilungen: 5147
  Beitrag No.51, eingetragen 2023-08-26

\quoteon(2023-08-26 22:10 - hyperG in Beitrag No. 50) Verwirrend \quoteoff Sind das nicht einfach die Unterschiede zwischen der System V amd64 ABI (siehe z.B. hier) und der Windows x64 ABI (siehe z.B. hier)?


   Profil
hyperG
Senior Letzter Besuch: in der letzten Woche
Dabei seit: 03.02.2017
Mitteilungen: 2159
  Beitrag No.52, eingetragen 2023-08-30

Da meine vielen Ideen zu sehr vom Ausgangsbeitrag abschweifen, geht es bei mir hier weiter: iterativer Benchmark "Mul, Add, Sub, cmp 256 Bit Int" in mehreren Sprachen


   Profil
juergenX
Aktiv Letzter Besuch: in der letzten Woche
Dabei seit: 08.07.2019
Mitteilungen: 942
  Beitrag No.53, vom Themenstarter, eingetragen 2023-08-31

\quoteon(2023-08-16 21:45 - polygamma in Beitrag No. 15) Hallo, Jürgen :) Ich habe eine Funktion geschrieben, die eine beliebige Binärfolge entgegennimmt und die Dezimaldarstellung berechnet. Wir sind hier zwar bis 256 bit unterwegs, aber ich dachte mir, dass es "allgemein" ein bisschen netter ist :) Liebe Grüße \quoteoff OK, ich woltte noch anmerken zu oben zitierten Beitrag #15 : Man koennete ja erst die die gewünschten DezimalFaktoren im Binaerstrings wandelt etwa $x = 2^{256} = "1 0000000 0000000 0000000 0000000 0000000 0000000 0000000 0000000 0000000 0000000 0000000 0000000 0000000 0000000 0000000 0000000b"$. und $x-1 = 2^{256}-1 = "0 1111111 1111111 1111111 1111111 1111111 1111111 1111111 1111111 1111111 1111111 1111111 1111111 1111111 1111111 1111111 1111111b"$. und $y = 2^{128} = "1 0000000 0000000 0000000 0000000 0000000 0000000 0000000 0000000 0000000 0000000 0000000 0000000 0000000 0000000 0000000 0000000b"$, und $y-1 = 2^{128} -1 = "0 1111111 1111111 1111111 1111111 1111111 1111111 1111111 1111111 1111111 1111111 1111111 1111111 1111111 1111111 1111111 111111b"$. $y-2 = 2^{128} -2 = "0 1111111 1111111 1111111 1111111 1111111 1111111 1111111 1111111 1111111 1111111 1111111 1111111 1111111 1111111 1111111 111110b"$, Dann die Binärdarstellungvon y-1 und y-2 Multiplizieren nach der Kastubra Methode z = (y-1,y-2) berechnen und zurückwandeln in Decimal Zahlen. Ich habe das extra wg. der Lesbarkei in 8er Blöcke unterteilt. Das Wandeln in Binaerstrngs dürfte recht flott gehen, die Multiplikation nicht. Ich muesste erst noch in cygwin die gmp_library und C++ importieren. Wenn x recht klein < 1024 = 2^10 ist, werden Multiplikationen leichter, auch wenn y gegen 2^245 geht fallen vielen einzel Aktionen bei z =(x * y) weg. EDIT 01.09 Die FFFFFFFFF blöcke sind ja nicht in (0,1)


   Profil
gonz
Senior Letzter Besuch: in der letzten Woche
Dabei seit: 16.02.2013
Mitteilungen: 4952
Wohnort: Harz
  Beitrag No.54, eingetragen 2023-09-01

Als Anmerkung hierzu noch... Wenn es "nur" um die Berechnung von Collatz Folgen geht, reicht die Multiplikation mit 3. Und diese läßt sich wahrscheinlich am einfachsten darstellen, indem man eine Kopie der Ausgangszahl durch Shiften mit 2 multipliziert und dann die Ausgangszahl addiert.


   Profil
juergenX
Aktiv Letzter Besuch: in der letzten Woche
Dabei seit: 08.07.2019
Mitteilungen: 942
  Beitrag No.55, vom Themenstarter, eingetragen 2023-09-02

Ja schön wäre das alles, hätte man ein shl (256) bit mit carry im Intel..


   Profil
juergenX
Aktiv Letzter Besuch: in der letzten Woche
Dabei seit: 08.07.2019
Mitteilungen: 942
  Beitrag No.56, vom Themenstarter, eingetragen 2023-09-02

ich komme noch mal auf die routine aus meinem Erstbeitrag zurück: \sourceon C typedef unsigned long long u64b; typedef __uint128_t u128b; typedef struct u256b u256b; struct u256b { u64b lo; u64b mid; u128b hi; }; u256b mul256b(u256b *x, u256b *y) { u128b t1 = (u128b) x->lo * y->lo; u128b t2 = (u128b) x->lo * y->mid; u128b t3 = x->lo * y->hi; u128b t4 = (u128b) x->mid * y->lo; u128b t5 = (u128b) x->mid * y->mid; u64b t6 = x->mid * y->hi; u128b t7 = x->hi * y->lo; u64b t8 = x->hi * y->mid; u64b lo = t1; u128b m1 = (t1 >> 64) + (u64b)t2; u64b m2 = m1; u128b mid = (u128b) m2 + (u64b)t4; u128b hi = (t2 >> 64) + t3 + (t4 >> 64) + t5 + ((u128b) t6 << 64) + t7 + ((u128b) t8 << 64) + (m1 >> 64) + (mid >> 64); u256b result = {lo, mid, hi}; return result; } \sourceoff Ich habe die pointer Aufrufe wenn nötig nicht verstanden. Wie sieht der main Programm aufruf aus? Was ist mul128b (usb1, usb2)? oder müssen da irgendwo sterne hin? Sei \sourceon C usb1.hi= 3; usb1.mid= 5; usb1.lo= 2^10; usb2.hi= 7; usb2.mid= 6; usb2.lo= 2^11; \sourceoff Ich kenne das von Pascal so etwa.. \sourceon pascal r=record x:integer; y:integer; end; var r1: r: var r2: r: var p1 : ^r1; var p2 : ^r2; r4:´= r1^ + r2^; \sourceoff ich verstehe das pointerkonzept in C oder C++ nicht.


   Profil
hyperG
Senior Letzter Besuch: in der letzten Woche
Dabei seit: 03.02.2017
Mitteilungen: 2159
  Beitrag No.57, eingetragen 2023-09-02

\quoteon(2023-09-02 20:12 - juergenX in Beitrag No. 56) ich komme noch mal auf die routine aus meinem Erstbeitrag zurück: \sourceon C typedef unsigned long long u64b; typedef __uint128_t u128b; typedef struct u256b u256b; struct u256b { u64b lo; u64b mid; u128b hi; }; u256b mul256b(u256b *x, u256b *y) { u128b t1 = (u128b) x->lo * y->lo; u128b t2 = (u128b) x->lo * y->mid; u128b t3 = x->lo * y->hi; u128b t4 = (u128b) x->mid * y->lo; u128b t5 = (u128b) x->mid * y->mid; u64b t6 = x->mid * y->hi; u128b t7 = x->hi * y->lo; u64b t8 = x->hi * y->mid; u64b lo = t1; u128b m1 = (t1 >> 64) + (u64b)t2; u64b m2 = m1; u128b mid = (u128b) m2 + (u64b)t4; u128b hi = (t2 >> 64) + t3 + (t4 >> 64) + t5 + ((u128b) t6 << 64) + t7 + ((u128b) t8 << 64) + (m1 >> 64) + (mid >> 64); u256b result = {lo, mid, hi}; return result; } \sourceoff Ich habe die pointer Aufrufe wenn nötig nicht verstanden. Wie sieht der main Programm aufruf aus? ... \quoteoff Das hatten wir doch schon im #8 unter https://matheplanet.com/matheplanet/nuke/html/viewtopic.php?rd2&topic=263288&start=0#p1913347 dass Ptr einer Variablen mit &Variablenname übergeben werden. und in #22 sieht man, dass man sich nicht mehr mit u256 als Zwischenvariable herumschlagen muss, da decimal_to_u256b bereits den nötigen Ptr liefert...


   Profil
juergenX
Aktiv Letzter Besuch: in der letzten Woche
Dabei seit: 08.07.2019
Mitteilungen: 942
  Beitrag No.58, vom Themenstarter, eingetragen 2023-09-23

Ich nochmal Mir ging es darum den code aus https://locklessinc.com/articles 256bit_arithmetic/ in C einzubinden. Wie aber genau? \sourceon asm mul256brute: mov (%rsi), %rax mov (%rdx), %r8 mov 0x8(%rdx), %r9 mov %rbp, -0x8(%rsp) mov %rax, %rbp mov 0x18(%rdx), %rcx imul %rbp, %rcx mov 0x10(%rdx), %r10 mul %r8 mov %rax, (%rdi) mov %rdx, %r11 mov %rbp, %rax mul %r10 mov %r12, -0x10(%rsp) mov %rax, %r12 add %rdx, %rcx mov %rbp, %rax mul %r9 add %rax, %r11 mov 0x8(%rsi), %rbp adc %rdx, %r12 adc $0, %rcx imul %rbp, %r10 mov %rbp, %rax mul %r8 add %rax, %r11 mov %rbp, %rax adc %rdx, %r12 adc %r10, %rcx mov 0x10(%rsi), %rbp mul %r9 mov %r11, 0x8(%rdi) imul %rbp, %r9 add %rax, %r12 mov %rbp, %rax adc %rdx, %rcx mov 0x18(%rsi), %rbp imul %r8, %rbp add %rbp, %rcx mov -0x8(%rsp), %rbp mul %r8 add %rax, %r12 mov %r12, 0x10(%rdi) mov -0x10(%rsp), %r12 adc %r9, %rcx add %rdx, %rcx mov %rcx, 0x18(%rdi) retq \sourceoff Korrekt aufzurufen aus C, nicht C++ , gmp, oder C#, weil ich dieses inline assembler doof finde. Wie ist das main prog? \sourceon C a = 123456789012345678906372467832647878594678954768954769; b = 987654321098765432106372467823467823647863726478324678; // mainprog decimal Mul2Integer { call mul256brute(a,b) return result } \sourceoff Das Ergebinis in dezimal Ziffern wäre am besten. Oder Binärstring oder Hexstring ist auch recht. Habe immer Probleme opder zu wenig Erfahrung mit der aufruf Syntax in C. Das call ist nicht so richtig ich weiß es eben nicht.. Ausgabe von result waere etwa dieser Art \sourceon C __int256 path lieget als dezimalZahl vor. void printf_256(__int256 path) { // Ausgabe int256 im Dezimalformat // jeweils 6 Ziffern durch Kommata getrennt int ziffer[52]; int cnt = 0; int loop; while (path>0) { ziffer[cnt]=path%10; path=path/10; cnt++; } for (loop = cnt-1;loop >= 0;loop--) { printf("%i",ziffer[loop]); if ( (loop%6 == 0) && (loop > 0) ) printf(","); } } \sourceoff Was genau also macht: \sourceon C mov (%rsi), %rax mov (%rdx), %r8 mov 0x8(%rdx), %r9 mov %rbp, -0x8(%rsp) mov %rax, %rbp mov 0x18(%rdx), %rcx \sourceoff Ich versuchee die Parameter übergabe (call by reference) im einzelnen nachzuvollziehen. Das 64 bit register %rax wir dahin gefüllt worauf %rsi zeigt? Das 64 bit register %r8 wird mit dem gefüllt worauf %rdx zeigt? Der Speicher auf den (%rdx+8) zeigt wird nach %r9 kopiert. Das waere dann der high 128 bit teil von was a oder b. Wir habe 3 pointer in rsi,rdx,rdx+8. Nach rax, r8,r9. diese (rsi,rdx) muessen ja irgendwo vorbereitet sein? So dass vom bp aus von unten gesehen pointer (%rdx+8), %r8, pointer(%rsi). (LIFO stack) benutzt werden Der andere faktor landet in sp+8,rbp,%rcx oder? Das minus bei mov %rbp, -0x8(%rsp) wundert mich. muss das nicht plus sein? Prinzipiell geht es um parameter übergabe by valuie or reference Wenn ich den ganzen mul256brute in ASM schreiben wollte, was ich am liebsten täte, Müsste ich 8 64 bit register pushen oder eben so vorgehn wie der Autor von "mul256brute" vorgeht Am ende steht das Ergebnis a*b auf dem Stack oder? \sourceon C add %rbp, %rcx mov -0x8(%rsp), %rbp mul %r8 add %rax, %r12 mov %r12, 0x10(%rdi) mov -0x10(%rsp), %r12 adc %r9, %rcx add %rdx, %rcx // das kommt mit falsch vor 2 Wete nach %rcx? mov %rcx, 0x18(%rdi) \sourceoff in 4 64 bit Blöcken auf dem Stack. So hofft man;) Die eigentlie Rechnerei nach der Karatsuba Mehode ist recht kurz. Es müssen Eingangs nur die pointer %rsi,%rdx,%rbp stimmen, die müssen auf was richtiges zeigen. Vielleich kann da mal jemand drüber schauen :) Ich Find die Verwending von $rbp und %rsp verwirrend.. Meine Frage ist,wie macht der C compiler das? offenbar richtig. Und könnte man an sich alles in Intel ASM schreiben!? Mir geht um das volle verstehen der parameter übergabe. und evt eine reine Intel 8086 ASM version. In dem anderen Threead find ich viele anregungen danke auch dafür. Wie einfach wäre es wenn x nur 1 Byte groß wäre! y = 123478875544545*3. Man muss mit call by pointer arbeiten um, auf 256 bit Werte zu verweisen. Thepretisch kann man so auch mit 512 bit oder längeren Faktoren arbeiten. Thx


   Profil
hyperG
Senior Letzter Besuch: in der letzten Woche
Dabei seit: 03.02.2017
Mitteilungen: 2159
  Beitrag No.59, eingetragen 2023-09-24

Da Du viele bereits beantwortete Dinge immer wieder neu fragst, und "Das Ergebinis in dezimal Ziffern wäre am besten...." (wir uns also im Kreise drehen ohne dabei vorwärts zu kommen) empfehle ich den bereits empfohlenen Code unter fast Karatsuba multiplication. Dort ist alles fertig und in beiden Welten (LINUX & Windows) sofort mit Dezimalzahlen lauffähig. Außerdem hatte ich bereits auf 2 neue Beiträge von mir hingewiesen, die auch zig Fragen beantworteten. Letzte Hilfe zu: Die Vermischung der LINUX-Welt & Windows Welt... und die Vermischung der ASM-Welt mit der C-Welt... Es gibt nicht den einen ASM-Compiler und c -Compiler, sondern zig in beiden Welten! Wenn Du alles 1:1 sofort lauffähig auf dem Tablet geliefert haben möchtest, brauchen wir von Dir: Betriebssystem: C-Compiler ASM-Compiler Bereits unter hier hatte ich die Register benannt, die das jeweilige Betriebssystem bei Funktionen übergeben werden. https://matheplanet.com/matheplanet/nuke/html/uploads/c/47407_ASM_Register_Tabelle.png Da wir 256Bit Strukturen an mehrere 64 Bit Register übergeben wollen, muss man also mit Ptr. arbeiten. Mit https://godbolt.org/ solltest Du Dir klar machen, was welcher Compiler in welcher Welt für einen Maschinencode erzeugt: a) Dein Ausgangs-LINK bezieht sich auf die LINUX-Welt mit gcc Compiler https://matheplanet.com/matheplanet/nuke/html/uploads/c/47407_ASM_Linux.png b) wenn Du aber in der Windows Welt bist, wo Intel-ASM gebräuchlicher ist, sieht ASM komplett anders aus: https://matheplanet.com/matheplanet/nuke/html/uploads/c/47407_ASM_Windows.png Dort sind die Übergabeparameter (also die Ptr., die auf die Strukturvariablen zeigen) also rcx, rdx, r8! Hinweis: da die beiden Compiler unterschiedlich gut optimieren, hat der obere gcc die beiden 64 Bit Teilbefehle mov qword ptr [r8], rdx mov qword ptr [r8 + 8], rax zu einem AVX-Befehl optimiert vmovdqu , denn dieser kann 128 Bit (oder 256 {oder sogar 512 bei meinem i9}) schnell von 128Bit Registern nach 128 Bit Variablen kopieren. Grüße


   Profil
hyperG
Senior Letzter Besuch: in der letzten Woche
Dabei seit: 03.02.2017
Mitteilungen: 2159
  Beitrag No.60, eingetragen 2023-09-24

Der ältere gcc erzeugt noch den einfachen ASM-Code ohne AVX Befehle: https://matheplanet.com/matheplanet/nuke/html/uploads/c/47407_ASM_Linux2.png Wenn Du jetzt Rückwärts ASM in einen C-Compiler mit hineinbringen willst, gibt es je nach Betriebssystem & Compiler mehrere Möglichkeiten: Linux: a) gcc & Mingcc haben inline-ASM b) externer ASM-Compiler erzeugt *.o Datei -> die kann gcc einbinden Win a) alter 32Bit VC konnte noch inline ASM b) 64 Bit Compiler können das meist nicht mehr -> externer ASM -> *.obj -> bei den Linker-Einstellungen diese obj angeben und im C-Code als extern"c" ... die Funktion bekannt machen. Der Linker sucht dann in der obj nach dem Code den er einbinden soll.


   Profil
juergenX
Aktiv Letzter Besuch: in der letzten Woche
Dabei seit: 08.07.2019
Mitteilungen: 942
  Beitrag No.61, vom Themenstarter, eingetragen 2023-09-25

\quoteon(2023-09-24 16:46 - hyperG in Beitrag No. 60) . . . Linux: a) gcc & Mingcc haben inline-ASM b) externer ASM-Compiler erzeugt *.o Datei -> die kann gcc einbinden Win a) alter 32Bit VC konnte noch inline ASM b) 64 Bit Compiler können das meist nicht mehr -> externer ASM -> *.obj -> bei den Linker-Einstellungen diese obj angeben und im C-Code als extern"c" ... die Funktion bekannt machen. Der Linker sucht dann in der obj nach dem Code den er einbinden soll. \quoteoff Extra vielen Dank nochmal!! Ich will noch nicht viel dazu sagen aber weil ich noch nicht alles voll gesichtet habe und nicht wieder doof fragen will. Methode b : mit externem w10 assembler, wie heiß der?, .Obj File aus den mul256brute s.o. erzeugen. Die Rahmen Procedur in gcc schreiben und dort im C-Code als extern"c" ... die Funktion bekannt machen. Dazu muss ich genau wissen wie die 8 (!) 64bit qwords bzw.3 Pointer (stimmt das?) (besser) vom Rahmenprogramm in gcc in das kompilat übergeben werden. Hast du ja auch viel zu gesagt :) Ein/ ausgabe ist in C leichter, wobei ich immer mit cygwin arbeite.. Also win 10, cygwin, externer assembler. Wie gesagt du hat im Grunde alles lang und breit erklärt:) Ich habe dies .obj einbinden aus turbo pascal 7.01 gut gekonnt sogar mit der 8087 FPU. asm = tasm Linker Tlink Debugger Tdebug Leider geht turbo pascal 7.01 nich mehr auf 64 bit prozessoren, dass ichs wuesste Oder man nimmt Lazarus. Aber C, C++, c sharp ist wohl unter win wie Linus heutiger standard. Also win 10, cygwin, externer assembler. Kein inline oder so nötich. Leider kann man kein mul xmm1,Xmm2,Xmm3 nutzen. Wenn man genau die Übergabe modalitäten vom Hauptsprche zu assember kennt, kann man vieles machen. Die Verknüpfung Main Prog in C, die aufwendige Rechnerei in ASM und rückgabe ins Main. Wie gesagt ich muss nochmal dein letztes verinnerlichen.. Melde mich dann. yours Jürgen Anm.: ist doch wahr das bei \sourceon C mul256brute: mov (%rsi), %rax mov (%rdx), %r8 mov 0x8(%rdx), %r9 \sourceoff immer move souce,destination gilt in Standard Intel oder umgekehrt ?


   Profil
juergenX
Aktiv Letzter Besuch: in der letzten Woche
Dabei seit: 08.07.2019
Mitteilungen: 942
  Beitrag No.62, vom Themenstarter, eingetragen 2023-09-28

\quoteon(2023-09-25 21:28 - juergenX in Beitrag No. 61) \sourceon C mul256brute: mov (%rsi), %rax mov (%rdx), %r8 mov 0x8(%rdx), %r9 \sourceoff immer move souce,destination gilt in Standard Intel oder umgekehrt ? \quoteoff OOOps natürlich ist es umgekehrt: in Standard Intel immer move destination, source! Deswegen obiger Beitrag #58 geändert. Wunsch meinerseits mit der Version rechte obere Grafik. https://matheplanet.com/matheplanet/nuke/html/uploads/c/47407_ASM_Linux.png hätte ich gerne mal ein "mundfertiges" C Prog gesehen, das x = 2361 183241 434822 606847 // = 2^71 -1 y = 9 223 372 036 854 775 811 // = 2^63 +3 x*y als Dezimal Zahl ausgibt ...


   Profil
hyperG
Senior Letzter Besuch: in der letzten Woche
Dabei seit: 03.02.2017
Mitteilungen: 2159
  Beitrag No.63, eingetragen 2023-09-28

Da Du immer noch am alten ASM-Code festhältst, der: - weder für Win - noch für TASM - noch für die meisten c-Compiler kompatibel ist... (Dann auch noch veraltet, da mit mehr als nur 4 nötige mul) ... und die bereits von polygamma vorgegebenen Wandlungsfunktionen... oder die GMP-Befehle ignorierst... ...bringt uns das kein mm vorwärts! (ich bezweifle sogar, dass TASM kompatible obj für cygwin erstellen kann; ASM ist nur dann nötig, wenn der Compiler hoch spezialisierte Befehle nicht versteht! Das ist etwa so, als wenn Du einen Raketenmotor in ein Spielzeugauto einbauen willst, aber keinerlei von Raketentechnik verstehst...) Also hier das "mundfertige" C Prog, was jeder c-Compiler sofort verstehen sollte: \showon \sourceon c #include #include #include #include //aus https://www.cs.cmu.edu/~cburch/251/karat/karat.txt #define MAX_DIGITS 1024 #define KARAT_CUTOFF 4 void gradeSchool(int *a, int *b, int *ret, int d) { int i, j; for (i = 0; i < 2 * d; i++) ret[i] = 0; for (i = 0; i < d; i++) { for (j = 0; j < d; j++) ret[i + j] += a[i] * b[j]; } } void karatsuba(int *a, int *b, int *ret, int d) { int i; int *ar = &a[0]; // low-order half of a int *al = &a[d / 2]; // high-order half of a int *br = &b[0]; // low-order half of b int *bl = &b[d / 2]; // high-order half of b int *asum = &ret[d * 5]; // sum of a's halves int *bsum = &ret[d * 5 + d / 2]; // sum of b's halves int *x1 = &ret[d * 0]; // ar*br's location int *x2 = &ret[d * 1]; // al*bl's location int *x3 = &ret[d * 2]; // asum*bsum's location if (d <= KARAT_CUTOFF) { gradeSchool(a, b, ret, d); return; } for (i = 0; i < d / 2; i++) { asum[i] = al[i] + ar[i]; bsum[i] = bl[i] + br[i]; } karatsuba(ar, br, x1, d / 2); karatsuba(al, bl, x2, d / 2); karatsuba(asum, bsum, x3, d / 2); // combine recursive steps for (i = 0; i < d; i++) x3[i] = x3[i] - x1[i] - x2[i]; for (i = 0; i < d; i++) ret[i + d / 2] += x3[i]; } void doCarry(int *a, int d) { int c; int i; c = 0; for (i = 0; i < d; i++) { a[i] += c; if (a[i] < 0) { c = -(-(a[i] + 1) / 10 + 1); } else { c = a[i] / 10; } a[i] -= c * 10; } if (c != 0) fprintf(stderr, "Overflow %d\n", c); } void getNum(int *a, int *d_a, char *strIn) { int c; int i; *d_a = 0; int iLen = strlen(strIn); while (*d_a < iLen) {//true c = strIn[*d_a];//getchar(); if (c == '\n' || c == EOF) break; if (*d_a >= MAX_DIGITS) { fprintf(stderr, "using only first %d digits\n", MAX_DIGITS); while (c != '\n' && c != EOF) c = getchar(); } a[*d_a] = c - '0'; ++(*d_a); } for (i = 0; i * 2 < *d_a - 1; i++) { c = a[i], a[i] = a[*d_a - i - 1], a[*d_a - i - 1] = c; } } void printNum(int *a, int d, char *strErg) { int i; char strH[22]; strErg[0] = 0; for (i = d - 1; i > 0; i--) if (a[i] != 0) break; for (; i >= 0; i--) { sprintf(strH,"%d", a[i]); strcat(strErg, strH); } } void Test_KaratsubaDezi(char *strA, char *strB, char *strErg) { int a[MAX_DIGITS+4]; // first multiplicand int b[MAX_DIGITS+4]; // second multiplicand int r[6 * MAX_DIGITS+4]; // result goes here int d_a; // length of a int d_b; // length of b int d; // maximum length int i; // counter getNum(a, &d_a, strA); getNum(b, &d_b, strB); if (d_a < 0 || d_b < 0) {//nur positive Zahlen printf("0\n"); exit(0); return;// 0; } i = (d_a > d_b) ? d_a : d_b; for (d = 1; d < i; d *= 2); for (i = d_a; i < d; i++) a[i] = 0; for (i = d_b; i < d; i++) b[i] = 0; karatsuba(a, b, r, d); doCarry(r, 2 * d); // now do any carrying printNum(r, 2 * d, strErg); } //main je nach Compiler: int main(int argc, char *argv[]) { char strErgebnis[3000]; char x_decimal[] = "2361183241434822606847";// char y_Dez[] = "9223372036854775811"; Test_KaratsubaDezi(x_decimal, y_Dez, strErgebnis); printf("Ergebnis= %s \n ", strErgebnis); //getchar(); warten auf Button-Klick, wenn man exe ohne Console startet } \sourceoff \showoff Grüße


   Profil
juergenX
Aktiv Letzter Besuch: in der letzten Woche
Dabei seit: 08.07.2019
Mitteilungen: 942
  Beitrag No.64, vom Themenstarter, eingetragen 2023-09-29

OK vielen Dank ! hier nutzt du gar kein asm weil du es überfllüssig "mächtig" hälts? Ich werde dein reines C mal step by step verstehen.. Meine Idee war die bruteforce method aus #58 in ein void main einzubinden in C Linux. die angeblich besser ist als das reine C compilat. Darum ging es ja in der Referenz: https://locklessinc.com/articles/256bit_arithmetic/ Über benschmarks wurde ja in den anderem thread lang und breit diskutiert. Tasm erwähnte ich nur weil man die .obj files leicht als external in Pascal Mains einbinden kann, womit ich früher gut und gerne arbeitete. OK erstmal :)


   Profil
hyperG
Senior Letzter Besuch: in der letzten Woche
Dabei seit: 03.02.2017
Mitteilungen: 2159
  Beitrag No.65, eingetragen 2023-10-02

juergenX, da selbst zu meinem "mundfertigen" C Programm wieder abweichende Fragen kommen (Wieder andere Compiler, wieder Gedankensprünge zu anderen Sprachen... wieder Sprünge zw. Win & LINUX...wieder der Code aus dem Internet statt der mundfertige...;) wird mir auch die private Antworterei zu viel. Was ist daran so schwer den vorgegebenen Code zu kopieren und unter https://onlinegdb.com/SwTwV3DzH eingeben und RUN klicken fertig: https://matheplanet.com/matheplanet/nuke/html/uploads/c/47407_Mundfertiges_c.PNG Zu den Teilfragen: - "welche Eingabe": genau deshalb hatte ich das Original für Dich umgeschrieben, damit die langen Zahlen nicht zur Laufzeit per Hand eingegeben werden müssen & alles kürzer und verständlicher wird - "gradeschool ": wenn die Argumente der Multiplikation klein genug sind, kann man die primitive Multiplikation mit der herkömmlichen Art (Schulmathematik) berechnen und die (Karat...)Rekursion somit beenden - "warum kein asm": weil asm nur für Befehle nötig wird, die der Compiler nicht unterstützt Bitte "den roten Faden" entlang hangeln und die Gedankengänge geradlinig halten. Solange nicht mal dieses primitive Programm (mit Dezimalziffern) verstanden wurde, sind Sprünge hin zu Hex-Arrays oder gar ASM absolut kontraproduktiv. Und lese https://de.wikipedia.org/wiki/Karazuba-Algorithmus dort steht beschrieben, mit welchem Trick man die Multiplikationsobergrenze der Argumente vergrößern kann (und so ein ÜBERLAUF verhindert, denn die CPU hat nur 64 Bit Register; selbst AVX nennt sich zwar 128, 256 & 512 Bit, aber bezüglich der Multiplikation sind alles nur parallele Teil-Register bis 64 Bit).


   Profil
juergenX
Aktiv Letzter Besuch: in der letzten Woche
Dabei seit: 08.07.2019
Mitteilungen: 942
  Beitrag No.66, vom Themenstarter, eingetragen 2023-10-02

Danke nochmal habe jetzt den sinn von Gradschool und getnum verstanden.. Thema Kollatz test: \sourceon C++ int main(int argc, char *argv[]) { char strErgebnis[3000]; char x_decimal[] = "2361183241434822606847";// 2^67-1 char y_Dez[] = "3"; Test_KaratsubaDezi(x_decimal, y_Dez, strErgebnis); printf("Ergebnis= %s \n ", strErgebnis); } \sourceoff \sourceon C++ 3*2361183241434822606847 (char x_decimal) = 7083549724304467820541 \sourceoff ist mit obiger aufwendiger Routine zu langsam...was klar war. (Kanonen nach Spatzen) Da ist wie gonz schon sagte shl und adc rax,1 sinnvoller, will das hier aber beschliessen. Tx hyper-g.man ;) for patience Jürgen


   Profil
juergenX hat die Antworten auf ihre/seine Frage gesehen.
juergenX hat selbst das Ok-Häkchen gesetzt.
Seite 2Gehe zur Seite: 1 | 2  

Wechsel in ein anderes Forum:
 Suchen    
 
All logos and trademarks in this site are property of their respective owner. The comments are property of their posters, all the rest © 2001-2023 by Matroids Matheplanet
This web site was originally made with PHP-Nuke, a former web portal system written in PHP that seems no longer to be maintained nor supported. PHP-Nuke is Free Software released under the GNU/GPL license.
Ich distanziere mich von rechtswidrigen oder anstößigen Inhalten, die sich trotz aufmerksamer Prüfung hinter hier verwendeten Links verbergen mögen.
Lesen Sie die Nutzungsbedingungen, die Distanzierung, die Datenschutzerklärung und das Impressum.
[Seitenanfang]