Warum haben x86-Designer (oder auch andere CPU-Architekturen) beschlossen, es nicht einzuschließen? Es ist ein Logikgatter, mit dem andere Logikgatter erstellt werden können, daher ist es schnell wie ein einzelner Befehl. Anstatt Verkettung not
und and
Anweisungen (beide werden aus erstellt nand
), warum keine nand
Anweisung?
52
BIC
Anweisung, die ista & ~b
. Arm Thumb-2 hat dieORN
Anweisung, die ist~(a | b)
. ARM ist ziemlich modern. Das Codieren eines Befehls im CPU-Befehlssatz hat seine Kosten. Also kommen nur die "nützlichsten" in ISA an.~(((a << 1) | (b >> 1)) | 0x55555555)
Unterricht haben. Der Zweck wäre, dass~(((a << 1) | (b >> 1)) | 0x55555555)
statt 6 nur eine Anweisung übersetzt werden kann. Warum also nicht?Antworten:
http://www.ibm.com/support/knowledgecenter/ssw_aix_61/com.ibm.aix.alangref/idalangref_nand_nd_instrs.htm : POWER hat NAND.
Aber im Allgemeinen sind moderne CPUs so aufgebaut, dass sie der automatisierten Codegenerierung durch Compiler entsprechen, und bitweises NAND wird sehr selten benötigt. Bitweises UND und ODER werden häufiger zum Bearbeiten von Bitfeldern in Datenstrukturen verwendet. Tatsächlich hat SSE AND-NOT, aber kein NAND.
Jeder Befehl hat Kosten in der Decodierlogik und verbraucht einen Opcode, der für etwas anderes verwendet werden könnte. Insbesondere bei Codierungen mit variabler Länge wie x86 können kurze Opcodes ausgehen und Sie müssen längere verwenden, was möglicherweise den gesamten Code verlangsamt.
quelle
if(windowType & ~WINDOW_RESIZABLE) { ... do stuff for variable-sized windows ... }
foo
es sich um ein uint64_t handelt, löscht die Anweisungfoo &= ~something;
manchmal mehr Bits als beabsichtigt. Wenn jedoch ein&~=
Operator vorhanden wäre, könnten solche Probleme vermieden werden.WINDOW_RESIZABLE
es sich um eine Konstante handelt, sollte ein Optimierer~WINDOW_RESIZABLE
zur Kompilierungszeit auswerten. Dies ist also nur ein UND zur Laufzeit.Der Aufwand für eine solche ALU-Funktion beträgt
1) die Logik, die die Funktion selbst ausführt
2) Der Selektor, der diese Funktion auswählt, resultiert anstelle der anderen aus allen ALU-Funktionen
3) die Kosten für das Vorhandensein dieser Option im Befehlssatz (und für das Fehlen einer anderen nützlichen Funktion)
Ich stimme Ihnen zu, dass die Kosten 1) sehr gering sind. Die Kosten für 2) und 3) sind jedoch nahezu unabhängig von der Funktion. Ich denke in diesem Fall waren die 3) Kosten (die in der Anweisung belegten Bits) der Grund, diese spezielle Anweisung nicht zu haben. Bits in einer Anweisung sind eine sehr knappe Ressource für einen CPU / Architektur-Designer.
quelle
Drehen Sie es um - sehen Sie zuerst, warum Nand im Hardware-Logik-Design beliebt war - es hat dort mehrere nützliche Eigenschaften. Dann fragen Sie, ob diese Eigenschaften in einer CPU-Anweisung noch zutreffen ...
TL / DR - tun sie nicht, es gibt also keinen Nachteil, stattdessen "And", "Or" oder "Not" zu verwenden.
Der größte Vorteil der festverdrahteten Nand-Logik war die Geschwindigkeit, die durch Verringern der Anzahl der Logikpegel (Transistorstufen) zwischen den Eingängen und Ausgängen einer Schaltung erzielt wurde. In einer CPU wird die Taktrate durch die Geschwindigkeit von viel komplexeren Operationen wie Addition bestimmt. Wenn Sie also eine AND-Operation beschleunigen, können Sie die Taktrate nicht erhöhen.
Und die Häufigkeit, mit der Sie andere Anweisungen kombinieren müssen, ist verschwindend gering - genug, damit Nand seinen Platz im Anweisungssatz wirklich nicht verdient.
quelle
Ich möchte Brian hier und Wouter und pjc50 zustimmen.
Ich möchte auch hinzufügen, dass für allgemeine Zwecke, insbesondere CISC-Prozessoren, Befehle nicht alle den gleichen Durchsatz haben - ein komplizierter Vorgang kann einfach mehr Zyklen erfordern als ein einfacher.
Betrachten Sie X86:
AND
(das ist eine "und" Operation) ist wahrscheinlich sehr schnell. Gleiches gilt fürNOT
. Lassen Sie uns einen Blick auf die Demontage werfen:Code eingeben:
Befehl zum Herstellen der Baugruppe:
Ausgabebaugruppe (verkürzt):
Wie Sie sehen können, werden bei Datentypen mit einer Größe von unter 64 einfach alle Dinge als Longs behandelt (daher das und l und nicht das l ), da dies anscheinend die "native" Bitbreite meines Compilers ist.
Die Tatsache, dass
mov
s dazwischen liegt, ist nur auf die Tatsache zurückzuführen, dasseax
es sich um das Register handelt, das den Rückgabewert einer Funktion enthält. Normalerweise rechnen Sie einfach imedi
Allzweckregister nach, um mit dem Ergebnis zu rechnen.Für 64 Bit ist es dasselbe - nur mit "Quad" -Wörtern (also nachgestellten
q
Wörtern) undrax
/rsi
anstelle voneax
/edi
.Es scheint, dass Intel für 128-Bit-Operanden und größere keine "Nicht" -Operation implementieren wollte. Stattdessen erstellt der Compiler ein All-
1
Register (Selbstvergleich des Registers mit sich selbst, im Register gespeichertes Ergebnis mit dervdcmpeqd
Anweisung) undxor
so weiter.Kurz gesagt: Durch die Implementierung einer komplizierten Operation mit mehreren elementaren Anweisungen wird die Operation nicht unbedingt verlangsamt. Es ist einfach kein Vorteil, wenn eine Anweisung mehrere Anweisungen ausführt, wenn sie nicht schneller ist.
quelle
Erstens, verwechseln Sie nicht bitweise und logische Operationen.
Bitweise Operationen werden normalerweise verwendet, um Bits in Bitfeldern zu setzen / löschen / umzuschalten / zu prüfen. Keine dieser Operationen erfordert nand ("and not", auch als "bit clear" bekannt, ist nützlicher).
Logische Verknüpfungen in den meisten modernen Programmiersprachen werden mit Kurzschlusslogik ausgewertet. Daher ist in der Regel ein branchenspezifischer Ansatz für die Implementierung erforderlich. Selbst wenn der Compiler feststellen kann, dass die Kurzschluss- / Gesamtauswertung keinen Unterschied zum Programmverhalten macht, sind die Operanden für die logischen Operationen normalerweise nicht in einer geeigneten Form, um den Ausdruck mit den bitweisen asm-Operationen zu implementieren.
quelle
NAND wird häufig nicht direkt implementiert, da die AND-Anweisung implizit die Möglichkeit bietet, auf eine NAND-Bedingung zu springen.
Das Ausführen einer logischen Operation in einer CPU setzt häufig Bits in einem Flagregister.
Die meisten Flag-Register haben ein ZERO-Flag. Das Null-Flag wird gesetzt, wenn das Ergebnis einer logischen Operation Null ist, und ansonsten gelöscht.
Die meisten modernen CPUs haben einen Sprungbefehl, der springt, wenn das Null-Flag gesetzt ist. Sie haben auch eine Anweisung, die springt, wenn das Null-Flag nicht gesetzt ist.
AND und NAND sind Ergänzungen. Wenn das Ergebnis einer AND-Operation Null ist, ist das Ergebnis einer NAND-Operation 1 und umgekehrt.
Wenn Sie also nicht springen möchten, wenn das NAND zweier Werte wahr ist, führen Sie einfach die UND-Operation aus und springen, wenn das Null-Flag gesetzt ist.
Wenn Sie also nicht springen möchten, wenn das NAND zweier Werte falsch ist, führen Sie einfach die UND-Operation aus und springen, wenn das Null-Flag gelöscht ist.
quelle
Nur weil etwas billig ist, heißt das nicht, dass es kostengünstig ist .
Wenn wir Ihre Argumentation ad absurdum nehmen, würden wir zu dem Schluss kommen, dass eine CPU größtenteils aus Hunderten von Varianten von NOP-Befehlen bestehen sollte - weil sie am billigsten zu implementieren sind.
Oder vergleichen Sie es mit Finanzinstrumenten: Würden Sie eine Anleihe im Wert von 1 USD mit einer Rendite von 0,01% kaufen, nur weil Sie dies können? Nein, Sie sparen lieber diese Dollars, bis Sie genug haben, um eine 10-Dollar-Anleihe mit besserer Rendite zu kaufen. Gleiches gilt für das Silikonbudget einer CPU: Es ist effektiv, viele billige, aber nutzlose Operationen wie NAND zu eliminieren und die gesparten Transistoren in etwas teureres, aber wirklich nützliches zu verwandeln.
Es gibt kein Rennen, um so viele Operationen wie möglich zu haben. Wie RISC gegen CISC bewiesen hat, was Turing von Anfang an wusste: Weniger ist mehr. Es ist eigentlich besser, so wenige Operationen wie möglich zu haben.
quelle
nop
kann nicht alle anderen Logikgatter implementieren, abernand
odernor
kann effektiv jeden Befehl neu erstellen, der in einer CPU in Software implementiert ist. Wenn wir den RISC-Ansatzgate
undinstruction
. Gates werden verwendet, um Anweisungen zu implementieren, nicht umgekehrt.NOP
ist eine Anweisung, kein Tor. Und ja, CPUs enthalten Tausende oder sogar Millionen von NAND-Gattern, um alle Anweisungen zu implementieren. Nur nicht die "NAND" -Anweisung.nand
ist ein Tor, das verwendet werden kann, um andere Tore zu implementieren; aber du hast schon alle anderen anweisungen . Das erneute Implementieren mithilfe einernand
Anweisung wäre langsamer . Und sie werden viel zu oft verwendet, um dies zu tolerieren, im Gegensatz zu Ihrem speziellen Beispiel, bei demnand
kürzere Codes erzeugt werden (nicht schnellerer Code, nur kürzerer). Aber das ist extrem selten und der Nutzen ist die Kosten einfach nicht wert.((((()))))
statt 5 einfach sagen können , oder? Fünf ist nur eine bestimmte Zahl, das ist viel zu einschränkend - Sätze sind viel allgemeiner: Pnand
implementiert alle Gates, kann also implizitnand
alle anderen Befehle implementieren. Wenn ein Programmierer dann einenand
Anweisung zur Verfügung hat, kann er seine eigenen Anweisungen erfinden, wenn er in Logikgattern denkt. Was ich von Anfang an gemeint habe, ist, dass ein Programmierer, wenn es so grundlegend ist, warum ihm keine eigene Anweisung gegeben wurde (dh ein Opcode in der Decoderlogik), eine solche Anweisung verwenden kann. Nachdem ich eine Antwort erhalten habe, weiß ich natürlich, dass dies von der Verwendung der Software abhängt.Auf Hardwareebene ist weder nand noch nor die elementare logische Verknüpfung. Abhängig von der Technologie (oder abhängig davon, was Sie willkürlich 1 und was Sie 0 nennen), kann entweder nand oder nor auf sehr einfache, elementare Weise implementiert werden.
Wenn wir den "nor" -Fall ignorieren, wird die gesamte andere Logik aus nand konstruiert. Aber nicht , weil es einige Computer wissenschaftlicher Beweis ist , dass alle logischen Operationen aus und konstruiert werden können - der Grund ist , dass es einfach ist , nicht jede elementare Methode xor zu bauen, oder, und etc. , das besser ist , dann ist es aus Nands Konstruktion.
Bei Computeranweisungen ist die Situation anders. Ein nand-Befehl könnte implementiert werden und wäre ein kleines bisschen billiger als beispielsweise die Implementierung von xor. Aber nur ein kleines bisschen, denn die Logik, die das Ergebnis berechnet, ist winzig im Vergleich zu der Logik, die den Befehl dekodiert, Operanden verschiebt, sicherstellt, dass nur eine Operation berechnet wird, und das Ergebnis aufnimmt und an die richtige Stelle liefert. Die Ausführung jedes Befehls dauert einen Zyklus. Dies entspricht einer Addition, die logisch zehnmal komplizierter ist. Die Einsparungen von nand gegenüber xor wären vernachlässigbar.
Was dann zählt, ist, wie viele Anweisungen für Operationen benötigt werden , die tatsächlich von typischem Code ausgeführt werden . Nand befindet sich bei weitem nicht ganz oben auf der Liste der häufig angeforderten Operationen. Es ist viel üblicher, dass und oder nicht angefordert werden. Entwickler von Prozessoren und Anweisungssätzen untersuchen viele vorhandene Codes und ermitteln, wie sich unterschiedliche Anweisungen auf diesen Code auswirken. Sie stellten höchstwahrscheinlich fest, dass das Hinzufügen eines nand-Befehls zu einer sehr geringen Verringerung der Anzahl von Prozessorbefehlen führen würde, die ausgeführt werden, um typischen Code auszuführen, und das Ersetzen eines vorhandenen Befehls durch nand die Anzahl der ausgeführten Befehle erhöhen würde.
quelle
Nur weil NAND (oder NOR) alle Gatter in kombinatorischer Logik implementieren kann, lässt sich dies nicht auf die gleiche Weise in einen effizienten bitweisen Operator übersetzen. Um ein UND nur mit NAND-Operationen zu implementieren, wobei c = a UND b ist, müsste c = a NAND b sein, dann b = -1, dann c = c NAND b (für ein NICHT). Die grundlegenden logischen bitweisen Operationen sind AND, OR, EOR, NOT, NAND und NEOR. Das ist nicht viel, und die ersten vier sind in der Regel sowieso eingebaut. In der Kombinationslogik sind die grundlegenden Logikschaltungen nur durch die Anzahl der verfügbaren Tore begrenzt, was ein völlig anderes Ballspiel ist. Die Anzahl der möglichen Verbindungen in einem programmierbaren Gate-Array, nach denen Sie wirklich suchen, ist in der Tat sehr hoch. Einige Prozessoren haben tatsächlich Gate-Arrays eingebaut.
quelle
Sie implementieren ein Logikgatter nicht, nur weil es funktionsfähig ist, insbesondere wenn die anderen Logikgatter nativ verfügbar sind. Sie implementieren, was von Compilern am häufigsten verwendet wird.
NAND, NOR und XNOR werden sehr selten benötigt. Neben den klassischen bitweisen Operatoren AND, OR und XOR hätte nur ANDN (
~a & b
) - das nicht NAND (~(a & b)
) ist - einen praktischen Nutzen. Wenn überhaupt, sollte eine CPU dies implementieren (und tatsächlich implementieren einige CPUs ANDN).Stellen Sie sich zum Erläutern des praktischen Nutzens von ANDN vor, Sie hätten eine Bitmaske, die viele Bits verwendet, aber Sie interessieren sich nur für einige der folgenden:
Normalerweise möchten Sie in der Bitmaske überprüfen, ob Ihre Bits von Interesse sind
Fangen wir an, indem wir Ihre Interessen zusammenfassen:
1. Alle Bits von Interesse werden gesetzt: bitweises ANDN + logisches NOT
Nehmen wir an, Sie möchten wissen, ob Ihre Interessen in Ordnung sind. Sie können es so sehen
(my_bitmask & IT_IS_FRIDAY) && (my_bitmask & IT_IS_WARM) && (my_bitmask & THE_SUN_SHINES)
. Jedoch normalerweise würden Sie das in zusammenbrechen2. Mindestens ein Bit von Interesse ist gesetzt: bitweises UND
Nehmen wir nun an, Sie möchten wissen, ob mindestens ein Bit von Interesse gesetzt ist. Sie können es als sehen
(my_bitmask & IT_IS_FRIDAY) || (my_bitmask & IT_IS_WARM) || (my_bitmask & THE_SUN_SHINES)
. Jedoch normalerweise würden Sie das in zusammenbrechen3. Mindestens ein Bit von Interesse ist nicht gesetzt: Bitweises ANDN
Angenommen, Sie möchten wissen, ob mindestens ein Bit von Interesse nicht gesetzt ist. Sie können es als sehen
!(my_bitmask & IT_IS_FRIDAY) || !(my_bitmask & IT_IS_WARM) || !(my_bitmask & THE_SUN_SHINES)
. Jedoch normalerweise würden Sie das in zusammenbrechen4. Es ist kein interessierendes Bit gesetzt: Bitweises UND + logisches NICHT
Nun lassen Sie uns sagen , dass Sie , wenn alle Bits von Interesse wissen wollen , sind nicht festgelegt. Sie können es als sehen
!(my_bitmask & IT_IS_FRIDAY) && !(my_bitmask & IT_IS_WARM) && !(my_bitmask & THE_SUN_SHINES)
. Jedoch normalerweise würden Sie das in zusammenbrechenDies sind die üblichen Operationen, die mit einer Bitmaske ausgeführt werden, sowie das klassische bitweise ODER und XOR. Ich glaube aber , dass eine Sprache (was nicht ist eine CPU ) sollte das bitweise NAND umfassen, NOR und XNOR Operatoren (deren Symbole wäre
~&
,~|
und~^
), obwohl nur selten verwendet. Ich würde nicht den ANDN Operator in einer Sprache , obwohl enthalten, da es nicht kommutativ ist (a ANDN b
ist nicht das gleiche wieb ANDN a
) - besser zu schreiben~a & b
statta ANDN b
, die ehemaligen zeigt deutlicher die asimmetry der Operation.quelle