Pourquoi les concepteurs x86 (ou d'autres architectures de CPU) ont-ils décidé de ne pas l'inclure? C'est une porte logique qui peut être utilisée pour construire d'autres portes logiques, elle est donc rapide comme une seule instruction. Plutôt que d'enchaîner not
et d' and
instructions (les deux sont créés à partir de nand
), pourquoi pas d' nand
instruction?.
52
BIC
instruction, qui esta & ~b
. Bras Thumb-2 a l'ORN
instruction qui est~(a | b)
. ARM est assez moderne. Le codage d’une instruction dans le jeu d’instructions de la CPU a un coût. Ainsi, seuls les plus "utiles" se frayent un chemin vers ISA.~(((a << 1) | (b >> 1)) | 0x55555555)
instructions. Le but serait de~(((a << 1) | (b >> 1)) | 0x55555555)
pouvoir traduire en une seule instruction au lieu de 6. Alors, pourquoi pas?Réponses:
http://www.ibm.com/support/knowledgecenter/ssw_aix_61/com.ibm.aix.alangref/idalangref_nand_nd_instrs.htm : L’alimentation possède une NAND.
Mais les processeurs généralement modernes sont conçus pour correspondre à la génération de code automatisée par les compilateurs, et la NAND au niveau du bit est très rarement requise. Les bits AND et OR s'utilisent plus souvent pour manipuler des champs de bits dans des structures de données. En fait, SSE a AND-NOT mais pas NAND.
Chaque instruction a un coût dans la logique de décodage et consomme un code opération qui pourrait être utilisé pour autre chose. Surtout dans les codages de longueur variable tels que x86, vous pouvez manquer de codes d'opération courts et devoir en utiliser des plus longs, ce qui ralentit potentiellement tout le code.
la source
if(windowType & ~WINDOW_RESIZABLE) { ... do stuff for variable-sized windows ... }
foo
est un uint64_t, l'instructionfoo &= ~something;
peut parfois effacer plus de bits que prévu, mais s'il y avait un&~=
opérateur, de tels problèmes pourraient être évités.WINDOW_RESIZABLE
est une constante, un optimiseur doit évaluer~WINDOW_RESIZABLE
au moment de la compilation, il ne s'agit donc que d'un AND au moment de l'exécution.Le coût d’une telle fonction ALU est de
1) la logique qui exécute la fonction elle-même
2) le sélecteur qui sélectionne ce résultat de fonction au lieu des autres parmi toutes les fonctions de l’ALU
3) le coût d’avoir cette option dans le jeu d’instructions (et de ne pas avoir d’autres fonctions utiles)
Je conviens avec vous que le 1) coût est très faible. Les coûts 2) et 3) sont toutefois presque indépendants de la fonction. Je pense que dans ce cas, le coût 3) (les bits occupés dans l'instruction) était la raison pour laquelle cette instruction spécifique n'était pas disponible. Les bits d'une instruction sont une ressource très rare pour un concepteur de CPU / architecture.
la source
Retournez-le - voyez d'abord pourquoi Nand était populaire dans la conception de la logique matérielle - il possède plusieurs propriétés utiles. Puis demandez si ces propriétés s'appliquent toujours dans une instruction de la CPU ...
TL / DR - ce n’est pas le cas, il n’ya donc aucun inconvénient à utiliser And, Or or Not à la place.
Le principal avantage de la logique câblée Nand était la vitesse, obtenue en réduisant le nombre de niveaux logiques (étages de transistors) entre les entrées et les sorties d'un circuit. Dans une CPU, la vitesse d'horloge est déterminée par la vitesse d'opérations beaucoup plus complexes telles que l'addition. Par conséquent, accélérer une opération AND ne vous permettra pas d'augmenter la fréquence d'horloge.
Et le nombre de fois que vous avez besoin de combiner d'autres instructions est extrêmement réduit, ce qui est suffisant pour que Nand ne gagne vraiment pas sa place dans le jeu d'instructions.
la source
Je voudrais être d'accord avec Brian ici, et Wouter et pjc50.
J'aimerais également ajouter que, sur les processeurs à usage général, en particulier les programmes CISC, les instructions n'ont pas toutes les mêmes débits - une opération compliquée peut simplement prendre plus de cycles que de simples.
Considérez X86:
AND
(qui est une opération "and") est probablement très rapide. Même chose pourNOT
. Regardons un peu de démontage:Code d'entrée:
Commande pour produire l'assemblage:
Ensemble de sortie (raccourci):
Comme vous pouvez le constater, pour les types de données de taille inférieure à 64, les choses sont simplement traitées comme des longs (d'où le et l et non l ), puisqu'il s'agit de la largeur de bit "native" de mon compilateur, semble-t-il.
Le fait qu'il y ait
mov
s entre les deux n'est dû qu'au fait queeax
le registre contient la valeur de retour d'une fonction. Normalement, vous devez simplement calculer dans leedi
registre général pour calculer le résultat.Pour 64 bits, c'est la même chose - juste avec des
q
mots "quad" (donc, finaux ) etrax
/rsi
au lieu deeax
/edi
.Il semble que pour les opérandes de 128 bits et plus, Intel n’a pas voulu implémenter une opération "non"; au lieu de cela, le compilateur produit un
1
registre complet (auto-comparaison du registre avec lui-même, résultat stocké dans le registre avec l'vdcmpeqd
instruction), etxor
cela.En bref: en implémentant une opération compliquée avec plusieurs instructions élémentaires, vous ne ralentissez pas nécessairement le fonctionnement. Il n’est tout simplement pas avantageux d’avoir une instruction qui exécute plusieurs instructions si elle n’est pas plus rapide.
la source
Tout d'abord, ne confondez pas les opérations au niveau du bit et logiques.
Les opérations au niveau des bits sont généralement utilisées pour définir / effacer / basculer / vérifier les bits dans les champs de bits. Aucune de ces opérations ne nécessite de nand ("et pas", aussi connu sous le nom de "bit clear" est plus utile).
Les opérations logiques dans la plupart des langages de programmation modernes sont évaluées à l'aide d'une logique de court-circuit. Il faut donc généralement une approche basée sur les branches pour les mettre en œuvre. Même lorsque le compilateur peut déterminer que l'évaluation par court-circuit par rapport à une évaluation complète ne change en rien le comportement du programme, les opérandes des opérations logiques ne sont généralement pas sous une forme commode pour implémenter l'expression à l'aide des opérations asm au niveau du bit.
la source
NAND n'est souvent pas implémenté directement parce que l'instruction AND vous donne implicitement la possibilité de sauter sur une condition NAND.
L'exécution d'une opération logique dans une CPU définit souvent des bits dans un registre d'indicateurs.
La plupart des registres de drapeaux ont un drapeau ZÉRO. L'indicateur zéro est défini si le résultat d'une opération logique est égal à zéro et est effacé dans le cas contraire.
La plupart des CPU modernes ont une instruction de saut qui saute si l'indicateur zéro est défini. Ils ont également une istruction qui saute si l'indicateur zéro n'est pas défini.
AND et NAND sont des compléments. Si le résultat d'une opération AND est égal à zéro, le résultat d'une opération NAND est égal à 1 et inversement.
Donc, si vous voulez sauter si le NAND de deux valeurs est vrai, exécutez simplement l'opération AND et sautez si l'indicateur zéro est défini.
Donc, si vous voulez sauter si la valeur NAND de deux valeurs est fausse, exécutez simplement l'opération AND et sautez si l'indicateur zéro est vide.
la source
Ce n'est pas parce que quelque chose est bon marché que c'est rentable .
Si nous prenons votre argumentation comme absurdum, nous conclurions qu’un processeur devrait être composé principalement de centaines de types d’instructions NOP, car elles sont les moins chères à mettre en œuvre.
Ou comparez-le à des instruments financiers: achèteriez-vous une obligation de 1 $ avec un rendement de 0,01% simplement parce que vous le pouvez? Non, vous préférez économiser ces dollars jusqu'à ce que vous en ayez assez pour acheter une obligation de 10 $ avec un meilleur rendement. Il en va de même avec le budget en silicone sur un processeur: il est efficace de supprimer de nombreux ops bon marché mais inutiles comme NAND, et de placer les transistors sauvegardés dans une configuration beaucoup plus chère mais réellement utile.
Il n'y a pas de course pour avoir autant d'opérations que possible. Comme RISC contre CISC avaient prouvé ce que Turing savait depuis le début: moins c'est plus. En fait, il vaut mieux avoir le moins d'opérations possible.
la source
nop
ne peut pas implémenter toutes les autres portes logiques, mais peutnand
ounor
peut effectivement recréer toute instruction implémentée dans une CPU dans un logiciel. Si nous adoptons l'approche RISC, c'est ..gate
etinstruction
. Les portes sont utilisées pour implémenter les instructions, pas l'inverse.NOP
est une instruction, pas une porte. Et oui, les processeurs contiennent des milliers voire des millions de portes NAND pour mettre en œuvre toutes les instructions. Juste pas l'instruction "NAND".nand
est une porte qui peut être utilisée pour mettre en œuvre d'autres portes; mais vous avez déjà toutes les autres instructions . Les réimplémenter en utilisant unenand
instruction serait plus lent . Et ils sont utilisés trop souvent pour tolérer cela, contrairement à votre exemple spécifique choisi par un cherry,nand
qui produirait un code plus court (pas plus rapide , mais plus court); mais c'est extrêmement rare, et le bénéfice n'en vaut tout simplement pas le coût.((((()))))
au lieu de 5, non? Cinq n'est qu'un nombre, c'est trop limitatif - les ensembles sont beaucoup plus généraux: Pnand
implémente toutes les portes, donc implicitementnand
peut implémenter toutes les autres instructions. Ensuite, si un programmeur a unenand
instruction disponible, il peut inventer ses propres instructions lorsqu'il pense à des portes logiques. Ce que je voulais dire dès le début, c’est que, si elle est aussi fondamentale, elle n’a pas reçu sa propre instruction (c’est-à-dire un opcode dans la logique du décodeur), afin qu’un programmeur puisse utiliser une telle instruction. Bien sûr, après avoir obtenu une réponse, je sais maintenant que cela dépend de l'utilisation du logiciel.Au niveau matériel, l'opération de logique élémentaire n'est ni ni ni. Selon la technologie (ou selon ce que vous appelez arbitrairement 1 et ce que vous appelez 0), ni nand ni ni ne peuvent être implémentés de manière très simple et élémentaire.
Si nous ignorons le cas "ni", toute autre logique est construite à partir de nand. Mais pas parce qu'il ya une preuve de l'informatique que toutes les opérations logiques peuvent être construits à partir de - la raison est qu'il ya juste ne importe quelle méthode élémentaire à construire XOR, ou, etc qui est mieux que le construire à partir nand de.
Pour les instructions informatiques, la situation est différente. Une instruction nand pourrait être implémentée, et cela coûterait un peu moins cher que de mettre en œuvre xor, par exemple. Mais seulement un tout petit peu, parce que la logique qui calcule le résultat est infime comparée à la logique qui décode l’instruction, déplace les opérandes, s’assure qu’une seule opération est calculée, récupère le résultat et le livre au bon endroit. Chaque instruction prend un cycle à exécuter, comme une addition qui est dix fois plus complexe en termes de logique. Les économies réalisées entre nand et xor seraient négligeables.
Ce qui compte alors, c'est le nombre d'instructions nécessaires pour les opérations réellement effectuées par du code typique . Nand est loin du haut de la liste des opérations couramment demandées. C'est beaucoup plus commun que et, ou, non sont demandés. Les concepteurs de processeurs et de jeux d'instructions examineront un grand nombre de codes existants et détermineront l'incidence des différentes instructions sur ce code. Ils ont très probablement constaté que l’ajout d’une instruction nand entraînerait une très faible réduction du nombre d’instructions de processeur exécutées pour exécuter un code typique, et que le remplacement d’une instruction existante par nand augmenterait le nombre d’instructions exécutées.
la source
Le fait que NAND (ou NOR) puisse implémenter toutes les portes en logique combinatoire ne se traduit pas de la même manière par un opérateur au niveau du bit efficace. Pour implémenter un AND en utilisant uniquement les opérations NAND, où c = a et b, vous devez avoir c = a NAND b, puis b = -1, puis c = c NAND b (pour un NON). Les opérations au niveau des bits de la logique de base sont AND, OR, EOR, NOT, NAND et NEOR. Ce n'est pas beaucoup à couvrir, et les quatre premiers sont généralement construits de toute façon. En logique combinatoire, les circuits logiques de base ne sont limités que par le nombre de portes disponibles, ce qui est un jeu de balle totalement différent. Le nombre d'interconnexions possibles dans un tableau de portes programmable, ce qui ressemble à ce que vous voulez vraiment, serait un très grand nombre. Certains processeurs ont effectivement des tableaux de portes intégrés.
la source
Vous n'implémentez pas une porte logique uniquement parce qu'elle a une fonctionnalité complète, en particulier si les autres portes logiques sont disponibles de manière native. Vous implémentez ce qui est le plus utilisé par les compilateurs.
NAND, NOR et XNOR sont très rarement nécessaires. Outre les opérateurs de bits classiques AND, OR et XOR, seul ANDN (
~a & b
) - qui n'est pas NAND (~(a & b)
) - aurait une utilité pratique. Le cas échéant, un processeur doit implémenter cela (et certains processeurs implémentent également ANDN).Pour expliquer l’utilité pratique d’ANDN, imaginez que vous disposiez d’un masque de masque utilisant de nombreux bits, mais que vous ne vous intéressiez qu’à certains d’entre eux, à savoir:
Normalement, vous voulez vérifier si le bitmask vous intéresse,
Commençons par rassembler vos éléments d’intérêt:
1. Tous les bits d’intérêt sont définis: AND au niveau du bit + NOT logique
Disons que vous voulez savoir si vos éléments d’intérêt sont tous définis. Vous pouvez le voir comme
(my_bitmask & IT_IS_FRIDAY) && (my_bitmask & IT_IS_WARM) && (my_bitmask & THE_SUN_SHINES)
. Cependant, normalement, vous réduiriez cela en2. Au moins un bit d’intérêt est défini: bitwise ET
Maintenant, disons que vous voulez savoir si au moins un élément d’intérêt est défini. Vous pouvez le voir comme
(my_bitmask & IT_IS_FRIDAY) || (my_bitmask & IT_IS_WARM) || (my_bitmask & THE_SUN_SHINES)
. Cependant, normalement, vous réduiriez cela en3. Au moins un bit d’intérêt n’est pas défini: bitwise ANDN
Maintenant, disons que vous voulez savoir si au moins un élément d’intérêt n’est pas défini. Vous pouvez le voir comme
!(my_bitmask & IT_IS_FRIDAY) || !(my_bitmask & IT_IS_WARM) || !(my_bitmask & THE_SUN_SHINES)
. Cependant, normalement, vous réduiriez cela en4. Aucun bit d’intérêt n’est défini: bitwise AND + logique NOT
Maintenant, disons que vous voulez savoir si tous les éléments d’intérêt ne sont pas définis. Vous pouvez le voir comme
!(my_bitmask & IT_IS_FRIDAY) && !(my_bitmask & IT_IS_WARM) && !(my_bitmask & THE_SUN_SHINES)
. Cependant, normalement, vous réduiriez cela enCe sont les opérations courantes effectuées sur un masque de bits, plus les OR classiques au niveau des bits et XOR. Je pense cependant qu'un langage (qui n'est pas un processeur ) devrait inclure les opérateurs au niveau du bit NAND, NOR et XNOR (dont les symboles seraient
~&
,~|
et~^
), bien qu'ils soient rarement utilisés. Cependant, je n'inclurais pas l'opérateur ANDN dans un langage, puisqu'il n'est pas commutatif (cea ANDN b
n'est pas la même chose queb ANDN a
) - il est préférable d'écrire~a & b
au lieu dea ANDN b
, l'ancien indique plus clairement l'asymétrie de l'opération.la source