Hallo Michael,
mschnell hat geschrieben:Überlegungen zur "FFT-Multiplikation".
Wir hatten in unserem Studium zwar die Fourier-Transformation, die FFT oder DFT hatten wir jedoch nicht durchgenommen. Wichtiger erscheinen mir zur Zeit die Low-Level-Optimierungen, die du in den vorhergehenden Beiträgen angesprochen hast. Aber trotzdem danke für die Infos. Hört sich an, als hättest du Mathematik studiert? Oder zumindest eine Wissenschaft, in der Mathematik zur Anwendung kommt
mschnell hat geschrieben:Zur Optimierung des internen Handlings sollte die Größe des Basis-Elements zur Compile-Zeit festliegen und je nach Prozessor 32 oder 64 Bit betragen. Entsprechen ist der Type des Basis-Elements entweder ein vorzeichenloser 32 Bit Integer oder ein vorzeichenloser 64 Bit Integer. Dann tritt die Größe des Basis-Elements bei den Berechnungen vermutlich überhaupt nicht mehr auf.
Ja, das ließe sich umsetzen denke ich. Zur Zeit ist das Basiselement bereits ein vorzeichenloses 32Bit-Integer (dword). Nur wird der Überschlag umständlich ausgeführt, hier kommt die Assembler-Optimierung.
Ich glaube, dass es nicht verkehrt ist als Langzahlen-Typ bei dynamischen Arrays von Basistyp-Elementen zu bleiben. Das Interface der Langzahlen-Unit nach außen wird sonst recht unübersichtlich.
Schon möglich. Hinsichtlich dynamischer Arrays gab es das setlength-Problem, welches besonders beim Karazuba-Algorithmus auftritt. Wie könnte eine Lösunge diesbezüglich aussehen?
Das Interface der Langzahlen-Unit nach außen sollte also die Grundfunktionen wie LongAdd und LongMult mit dynamischen Arrays zur Verfügung stellen. Da die Karatsuba-Multiplikation aber Funktionen auf Teil-Arrays ausführt, sollte intern mit Zeigern (Var-Parametern) gearbeitet werden. Hier würde ein Funktions-Aufruf rein mit dynamischen Arrays ein Kopieren das Arrays erfordern.
Zur Zeit sieht die verwendete Karazuba-Funktion ähnlich aus, wie du sie beschreibst:
Code: Alles auswählen
function GNZKarazuba(a,b:GNZTyp;aAnz,bAnz,Laenge:dword):GNZTyp;
Nur wird zusätzlich die Stellenanzahl von a und b, aAnz und bAnz, übertragen. Das erspart ein paar length-Aufrufe.
Die Karazuba-Multiplikation funktioniert also ähnlich, wie du es vorschlägst. Die "normale" Multiplikation sowie die Addition dagegen noch nicht, hier wäre es sinnvoll, folgende, immer wieder auftretende Schleife so wie du es vorschlägst in eine AddInternal auszulagern und in Assembler zu schreiben (siehe unten nochmal detailierter):
Code: Alles auswählen
ZwSp:= a[n] + b[n]; //Addition des LongNumberBaseType (in diesem Fall dword)
If ZwSp>=GNZ_GlobZahlenbasis then // GlobZahlenbasis ist 2^32, also gleich der Größe des dword. Hier beginnt also die Berechnung des Überschlags.
begin
Erg[n]:= ZwSp-GNZ_GlobZahlenbasis;
ZwSp:=1;
end else
begin
Erg[n]:=ZwSp;
ZwSp:=0;
end;
Die GlobZahlenbasis ist 2^32. Damit könnte man diese Variable durch Verwendung von Assembler einsparen, denn die GlobZahlenbasis erfüllt keinen anderen Sinn als den Überschlag zu berechnen, welcher jedoch über Assembler direkt berechnet werden kann.
Karatsuba benötigt neben der Multiplikation noch die Addition und (wenn ich das richtig sehe) die Operation z := a - b - c; und z := a + b + c;. Addition ist als LongAddInternal ohnehin vorhanden
Ja genau.
die anderen Operationen wären dann
Code: Alles auswählen
procedure LongSubSubInternal(var Summe, s1, s2, s3: LongNumberBaseType; Length: Integer);
Stimme zu.
Wenn das läuft kann man sich daranmachen LongAddImnternal, LongSubSubInternal, LongAddAddInternal und schließlich LongMultInternalPrimitive zunächst in der 32 Bit Variante in ASM umzuschreiben und bei entsprechend gesetzter Compiler-Option statt der Pascal-Version zu verwenden. Danach können auch die 64 Bit Assembler Teile in Angriff genommen werden.
Hört sich das sinnvoll an ?
Auf jeden Fall. Möchtest du mal versuchen, die Prozedur LongAddInternal umzusetzen? Am Besten zunächst für 32bit, also für den Dateityp dword. Dazu müsste der folgende Pascal-Quelltext in Assembler übersetzt werden:
Code: Alles auswählen
procedure LongAddInternal32(var Summe, Ueberschlag, Summand1, Summand2: dword);
begin
Summe:=Summand1+Summand2; //(1)
//Soweit sollte alles klar sein. Nun muss noch der Überschlag berechnet werden. Die Berechnung des Überschlags erfolgt nach deiner Aussage in Assembler gleichzeitig mit (1). In Pascal ist folgender (langsamer!) Quelltext dafür nötig:
If Summe >2^32 then
begin
Ueberschlag:=1;
Summe:=Summe-2^32;
end else Ueberschlag:=0;
//Die Variable Ueberschlag kann also die Werte 0 und 1 annehmen.
end;
Ich könnte eine solche Prozedur direkt in die Arithmetik-Unit einbauen. Dadurch würde die GNZ_GlobZahlenbasis hinsichtlich der Addition schonmal wegfallen. Zudem wäre viel Zeit gewonnen.
Anschließend könnten wir diesen Schritt auch für die primitive Multiplikation wagen, wodurch die GNZ_GlobZahlenbasis auch aus den Multiplikations-Routinen verschwinden würden, und so die Gnurz-Unit langsam GlobZahlenbasis-frei wird.
Viele Grüße, Euklid