Ich arbeite an Arithmetik für die Multiplikation sehr langer Ganzzahlen (einige 100.000 Dezimalziffern). Als Teil meiner Bibliothek füge ich zwei lange Zahlen hinzu.Beschleunigung x64 Assembler ADD Schleife
Profiling zeigt, dass mein Code bis zu 25% seiner Zeit in den add() und sub() Routinen läuft, also ist es wichtig, dass sie so schnell wie möglich sind. Aber ich sehe noch nicht viel Potenzial. Vielleicht können Sie mir Hilfe, Rat, Einsicht oder Ideen geben. Ich werde sie testen und zu dir zurückkommen.
Bisher hat meine add-Routine einige Setup und verwendet dann eine 8-mal abgerollt Schleife:
mov rax, QWORD PTR [rdx+r11*8-64]
mov r10, QWORD PTR [r8+r11*8-64]
adc rax, r10
mov QWORD PTR [rcx+r11*8-64], rax
7 weitere Blöcke mit unterschiedlichen Offsets folgen und dann in einer Schleife es.
Ich habe versucht, die Werte aus dem Speicher früher zu laden, aber das hat nicht geholfen. Ich denke, das liegt an einem guten Prefetching. Ich benutze eine Intel i7-3770 Ivy Bridge 4-Core-CPU. Aber ich möchte Code schreiben, der auf jeder modernen CPU gut funktioniert.
Bearbeiten: Ich habe einige Timings: Es fügt 1k Wörter in etwa 2,25 Zyklen/Wort. Wenn ich den ADC entferne, so bleiben nur die MOVs übrig, es dauert immer noch etwa 1,95 Zyklen/Wort. Der größte Engpass scheint also der Speicherzugriff zu sein. Eine Bibliothek memcpy()
arbeitet in etwa 0,65 Zyklen/Wort, hat aber nur einen Eingang, nicht zwei. Trotzdem ist es viel schneller wegen der Verwendung von SSE-Registern, denke ich.
Einige Fragen:
- Ist es sinnvoll Struktur zu verwenden, "Last, Last, einzugeben, zu speichern" oder wäre eine "Last, Add-to-memory" helfen? Bisher zeigten meine Tests keine Vorteile.
- Wie üblich, gibt es keine Hilfe von SSE (2,3,4) zu erwarten?
- Beeinträchtigt die Adressierung (skalierter Index plus Basis plus Offset) stark? Ich könnte stattdessen
ADD r11, 8
verwenden. - Was ist mit der Schleife abrollen? Ich habe gelesen, dass das Abrollern für die Sandy-Bridge-Architektur schlecht war (Agner Fog http://www.agner.org/optimize/). Soll es bevorzugt oder vermieden werden?
- (Bearbeiten) Kann ich SSE-Register verwenden, um Wörter in größeren Blöcken aus dem Speicher zu laden und zu speichern und effizient Wörter mit allgemeinen Registern und SSE-Registern auszutauschen?
Ich freue mich über Kommentare.
Der schnellste Weg (ich weiß) zu sehr große Zahl zu multiplizieren ist eine schnelle Fourier-Transformation http://en.wikipedia.org/wiki/Multiplication_algorithm Ich habe nie versucht, seine Logik in Assembler zu implementieren. Anscheinend enthält Prime95 eine schnelle Fourier-Transformation in der x86-Assembly-Logik und Sie können es (frei) von dort aus nehmen. –
Danke, ich weiß darüber Bescheid. Im Moment möchte ich nur schnell hinzufügen. – cxxl
Sie können GMP-Quellen einsehen. – zch