Die Aufgabe ist einfach: Schreiben Sie eine Assembly, die die Größenordnung einer Ganzzahl mit möglichst wenigen Taktzyklen berechnet.
- Größenordnung ist definiert als
log10
, nichtlog2
. - Der gültige Eingabebereich ist
0
bis einschließlich. Das Eingabeverhalten außerhalb dieses Bereichs ist undefiniert.1012
- Die Werte sollten auf die nächste Ganzzahl abgerundet werden, mit der Ausnahme, dass bei einer bestimmten Eingabe
0
die Ausgabe erfolgen sollte0
. (Sie können dies als die beste Darstellung der negativen Unendlichkeit betrachten, die in vorzeichenlosen ganzen Zahlen möglich ist). - Muss x86-Assembly sein.
- Die Ganzzahl muss ein Laufzeitwert sein, keine statische / inline Ganzzahl. Wir wissen also nicht, was es zur Kompilierungszeit ist.
- Angenommen, Sie haben bereits eine Ganzzahl in ein Register geladen. (Setzen Sie jedoch den Wert in das Register in der Antwort ein, um die Übersichtlichkeit zu gewährleisten.)
- Es können keine externen Bibliotheken oder Funktionen aufgerufen werden.
- Befreien Sie sich von den verfügbaren Anweisungen in den Intel-Dokumenten .
- Nein C.
- Jede der ~ 7 Intel Core-Architekturen ist akzeptabel (siehe Seite 10 ). Idealerweise Nehalem (Intel Core i7).
Die gewinnende Antwort ist eine, die die wenigsten möglichen Taktzyklen verwendet. Das heißt, es können die meisten Vorgänge pro Sekunde ausgeführt werden. Ungefähre Zusammenfassungen der Taktzyklen finden Sie hier: http://www.agner.org/optimize/instruction_tables.pdf . Die Berechnung von Taktzyklen kann erfolgen, nachdem die Antwort gesendet wurde.
math
fastest-algorithm
assembly
Lance Pollard
quelle
quelle
Antworten:
7 Zyklen, konstante Zeit
Hier ist eine Lösung, die auf meiner Antwort auf diese SO-Frage basiert . Es verwendet BSR, um zu zählen, wie viele Bits benötigt werden, um die Nummer zu halten. Es wird nachgeschlagen, wie viele Dezimalstellen erforderlich sind, um die größte Zahl darzustellen, die viele Bits enthalten können. Dann wird 1 abgezogen, wenn die tatsächliche Zahl kleiner ist als die nächste Zehnerpotenz mit so vielen Stellen.
Kompiliert unter GCC 4.6.3 für Ubuntu und gibt den Wert im Exit-Code zurück.
Ich bin nicht sicher, ob ich diese Zyklustabelle für einen modernen Prozessor interpretieren kann, aber wenn ich die @ DigitalTrauma-Methode auf dem Nehalim-Prozessor verwende, erhalte ich 7 ?
quelle
The integer must be a runtime value, not a static/inline integer. So we don't know what it is at compile time.
Bester Fall 8 Zyklen, schlechtester Fall 12 Zyklen
Da es in der Frage nicht klar ist, begründe ich dies mit den Ivy Bridge-Latenzen.
Der Ansatz besteht hier darin, die
bsr
Anweisung (Bit-Scan-Umkehrung) als log2 () eines armen Mannes zu verwenden. Das Ergebnis wird als Index für eine Sprungtabelle verwendet, die Einträge für die Bits 0 bis 42 enthält. Ich gehe davon aus, dass die Verwendung desbsr
Befehls OK ist , wenn die Operation für 64-Bit-Daten implizit erforderlich ist.Im besten Fall kann der Eintrag jumptable die
bsr
Ergebnis direkt auf die Größe . Beispiel: Für Eingaben im Bereich 32-63 ist dasbsr
Ergebnis 5, was einer Größe von 1 zugeordnet wird. In diesem Fall lautet der Befehlspfad:Im schlimmsten Fall wird das
bsr
Ergebnis auf zwei mögliche Größen abgebildet, sodass der Eintrag für die Sprungtabelle eine weitere machtcmp
, um zu überprüfen, ob die Eingabe> 10 n ist . ZB für Eingaben im Bereich 64-127 ist dasbsr
Ergebnis 6. Der entsprechende Sprungtabelleneintrag prüft dann, ob die Eingabe> 100 ist, und stellt die Ausgabegröße entsprechend ein.Zusätzlich zum Worst-Case-Pfad haben wir einen zusätzlichen mov-Befehl, um einen 64-Bit-Direktwert für die Verwendung in zu laden.
cmp
Der Worst-Case-Befehlspfad lautet also:Hier ist der Code:
Dies wurde hauptsächlich aus der gcc-Assembler-Ausgabe für den von mir geschriebenen Proof-of-Concept-C-Code generiert . Beachten Sie, dass der C-Code ein berechenbares goto verwendet, um die Sprungtabelle zu implementieren. Es verwendet auch die
__builtin_clzll()
gcc verwendet, das zurbsr
Anweisung kompiliert wird (plus einxor
).Ich habe mehrere Lösungen in Betracht gezogen, bevor ich zu dieser gekommen bin:
FYL2X
um dann den natürlichen log zu berechnenFMUL
durch die notwendige Konstante. Dies würde vermutlich gewinnen, wenn es ein [tag: instruction: golf] -Wettbewerb wäre.FYL2X
Hat aber so eine Latenz von 90-106 für Ivy Bridge.Hardcodierte binäre Suche. Dies kann tatsächlich wettbewerbsfähig sein - ich überlasse es jemand anderem, dies umzusetzen :).
Komplette Nachschlagetabelle der Ergebnisse. Ich bin sicher, dass dies theoretisch am schnellsten ist, würde aber eine 1-TB-Nachschlagetabelle erfordern - noch nicht praktisch -, möglicherweise in ein paar Jahren, wenn Moores Gesetz weiterhin gilt.
quelle
jmp
Undjcc
keine Latenz, nur Durchsatzkosten. Verzweigungsvorhersage und spekulative Ausführung bedeuten, dass Kontrollabhängigkeiten nicht Teil von Datenabhängigkeitsketten sind.