Dlaczego projektanci x86 (lub inne architektury procesorów) zdecydowali się go nie uwzględniać? Jest to bramka logiczna, której można użyć do budowy innych bram logicznych, dlatego jest szybka jak pojedyncza instrukcja. Zamiast tworzenia łańcuchów not
i and
instrukcji (oba są tworzone nand
), dlaczego nie ma nand
instrukcji ?.
52
BIC
instrukcję, która jesta & ~b
. Ramię Thumb-2 maORN
instrukcję, która jest~(a | b)
. ARM jest dość nowoczesny. Kodowanie instrukcji w zestawie instrukcji CPU ma swoje koszty. Tak więc tylko najbardziej „przydatne” pojawiają się w ISA.~(((a << 1) | (b >> 1)) | 0x55555555)
instrukcje. Celem byłoby, aby~(((a << 1) | (b >> 1)) | 0x55555555)
można było przetłumaczyć je na jedną instrukcję zamiast na 6. Więc dlaczego nie?Odpowiedzi:
http://www.ibm.com/support/knowledgecenter/ssw_aix_61/com.ibm.aix.alangref/idalangref_nand_nd_instrs.htm : POWER ma NAND.
Ale generalnie nowoczesne procesory są zbudowane tak, aby pasowały do automatycznego generowania kodu przez kompilatory, a bitowa NAND jest bardzo rzadko wymagana. Bitowe AND i OR częściej wykorzystywane są do manipulowania polami bitowymi w strukturach danych. W rzeczywistości SSE ma AND-NOT, ale nie NAND.
Każda instrukcja ma swój koszt w logice dekodowania i zużywa kod operacji, którego można użyć do czegoś innego. Zwłaszcza w kodowaniu o zmiennej długości, takim jak x86, możesz skończyć z krótkimi kodami i użyć dłuższych, co może spowolnić cały kod.
źródło
if(windowType & ~WINDOW_RESIZABLE) { ... do stuff for variable-sized windows ... }
foo
jest to uint64_t, instrukcjafoo &= ~something;
może czasami wyczyścić więcej bitów niż zamierzono, ale gdyby istniał&~=
operator, problemów takich można by uniknąć.WINDOW_RESIZABLE
jest stałą, to optymalizator powinien oceniać~WINDOW_RESIZABLE
w czasie kompilacji, więc jest to po prostu AND w czasie wykonywania.Koszt takich funkcji ALU wynosi
1) logika, która wykonuje samą funkcję
2) selektor, który wybiera tę funkcję zamiast innych spośród wszystkich funkcji ALU
3) koszt posiadania tej opcji w zestawie instrukcji (i braku innych przydatnych funkcji)
Zgadzam się z tobą, że 1) koszt jest bardzo mały. Koszt 2) i 3) jest jednak prawie niezależny od funkcji. Myślę, że w tym przypadku 3) koszt (bity zajmowane w instrukcji) były powodem braku takiej konkretnej instrukcji. Bity w instrukcji są bardzo rzadkim zasobem dla projektanta procesora / architektury.
źródło
Odwróć to - najpierw sprawdź, dlaczego Nand był popularny w projektowaniu logiki sprzętowej - ma tam kilka przydatnych właściwości. Następnie zapytaj, czy te właściwości nadal mają zastosowanie w instrukcji procesora ...
TL / DR - nie robią tego, więc nie ma wady używania And, Or or Not zamiast tego.
Największą zaletą przewodowej logiki Nand była szybkość, uzyskana dzięki zmniejszeniu liczby poziomów logicznych (stopni tranzystorowych) między wejściami i wyjściami obwodu. W CPU szybkość zegara zależy od prędkości znacznie bardziej złożonych operacji, takich jak dodawanie, więc przyspieszenie operacji AND nie pozwoli na zwiększenie częstotliwości taktowania.
Liczba przypadków, w których musisz łączyć inne instrukcje, jest znikomo mała - wystarczająca, aby Nand naprawdę nie zajmował miejsca w zestawie instrukcji.
źródło
Chciałbym się tutaj zgodzić z Brianem, Wouterem i pjc50.
Chciałbym również dodać, że w przypadku procesorów ogólnego przeznaczenia, zwłaszcza procesorów CISC, instrukcje nie wszystkie mają taką samą przepustowość - skomplikowana operacja może po prostu zająć więcej cykli niż łatwa.
Rozważ X86:
AND
(która jest operacją „i”) jest prawdopodobnie bardzo szybka. To samo dotyczyNOT
. Spójrzmy na trochę demontażu:Kod wejściowy:
Polecenie wykonania złożenia:
Zespół wyjściowy (skrócony):
Jak widać, dla typów danych mniejszych niż 64, wszystkie rzeczy są po prostu obsługiwane jako długie (stąd i l, a nie l ), ponieważ, jak się wydaje, jest to „natywna” przepustowość mojego kompilatora.
Fakt, że jest
mov
między nimi wynika tylko z faktu, żeeax
jest to rejestr zawierający wartość zwracaną przez funkcję. Zwykle wystarczy obliczyć wedi
rejestrze ogólnego przeznaczenia, aby obliczyć wynik.W przypadku 64 bitów jest tak samo - tylko z „quad” (stąd końcowymi
q
) słowami irax
/rsi
zamiasteax
/edi
.Wygląda na to, że dla 128-bitowych operandów i większych Intel nie dbał o wdrożenie operacji „nie”; zamiast tego kompilator tworzy
1
rejestr ogólny (samo porównanie rejestru z samym sobą, wynik zapisany w rejestrze zvdcmpeqd
instrukcją) i takxor
jest.W skrócie: Implementując skomplikowaną operację z wieloma instrukcjami elementarnymi, niekoniecznie spowalniasz operację - po prostu nie ma korzyści z posiadania jednej instrukcji, która wykonuje wiele instrukcji, jeśli nie jest szybsza.
źródło
Po pierwsze, nie myl bitowych i logicznych operacji.
Operacje bitowe są zwykle używane do ustawiania / usuwania / przełączania / sprawdzania bitów w polach bitowych. Żadna z tych operacji nie wymaga nand (bardziej przydatne jest „i nie”, znane również jako „bit clear”).
Operacje logiczne w większości współczesnych języków programowania są oceniane przy użyciu logiki zwarciowej. Dlatego zazwyczaj potrzebne jest podejście oparte na oddziałach. Nawet jeśli kompilator może stwierdzić, że zwarcie w porównaniu z całkowitą oceną nie ma znaczenia dla zachowania programu, operandy operacji logicznych zwykle nie są w wygodnej formie do implementacji wyrażenia za pomocą bitowych operacji asm.
źródło
NAND często nie jest implementowany bezpośrednio, ponieważ posiadanie instrukcji AND daje ci możliwość przeskoczenia na warunek NAND.
Wykonywanie operacji logicznej w CPU często ustawia bity w rejestrze flag.
Większość rejestrów flag ma flagę ZERO. Flaga zerowa jest ustawiana, jeśli wynikiem operacji logicznej jest zero, i w przeciwnym razie jest kasowana.
Większość współczesnych procesorów ma instrukcję skoku, która skacze, jeśli ustawiona jest flaga zerowa. Mają także instrukcję, która przeskakuje, jeśli flaga zerowa nie jest ustawiona.
AND i NAND są uzupełnieniami. Jeżeli wynik operacji AND wynosi zero, to wynikiem operacji NAND jest 1 i odwrotnie.
Więc jeśli chcesz przeskoczyć, jeśli NAND dwóch wartości jest prawdziwy, po prostu wykonaj operację AND i przeskocz, jeśli ustawiona jest flaga zero.
Więc jeśli chcesz przeskoczyć, jeśli NAND dwóch wartości jest fałszywy, po prostu wykonaj operację AND i przeskocz, jeśli flaga zerowa jest czysta.
źródło
To, że coś jest tanie , nie oznacza, że jest opłacalne .
Jeśli weźmiemy twoją argumentację ad absurdum, dojdziemy do wniosku, że procesor powinien składać się głównie z setek odmian instrukcji NOP - ponieważ są one najtańsze do wdrożenia.
Lub porównaj to z instrumentami finansowymi: czy kupiłbyś obligację 1 $ z zyskiem 0,01% tylko dlatego, że możesz? Nie, wolisz oszczędzać te dolary, dopóki nie będziesz mieć dość, aby kupić 10 USD obligacji z lepszym zwrotem. To samo dotyczy silikonu budżetowego na procesor: efektywnie wypiera wiele tanich, ale bezużytecznych operacji, takich jak NAND, i zapisuje zapisane tranzystory w coś znacznie droższego, ale naprawdę przydatnego.
Nie ma rasy, która miałaby jak najwięcej operacji. Jak RISC kontra CISC udowodniły, co Turing wiedział od samego początku: mniej znaczy więcej. Tak naprawdę lepiej mieć jak najmniej operacji.
źródło
nop
nie może zaimplementować wszystkich innych bramek logicznych, alenand
lubnor
może skutecznie odtworzyć dowolną instrukcję zaimplementowaną w procesorze w oprogramowaniu. Jeśli weźmiemy podejście RISC, to znaczy ...gate
iinstruction
. Bramki służą do wdrażania instrukcji, a nie na odwrót.NOP
jest instrukcją, a nie bramą. I tak, procesory zawierają tysiące, a może nawet miliony bramek NAND do implementacji wszystkich instrukcji. Po prostu nie instrukcja „NAND”.nand
to jedna brama, którą można wykorzystać do realizacji innych bram; ale masz już wszystkie pozostałe instrukcje . Wdrożenie ich za pomocąnand
instrukcji byłoby wolniejsze . I są one używane zbyt często, aby to tolerować, w przeciwieństwie do wybranego przez ciebie konkretnego przykładu, w którymnand
produkuje się krótszy kod (nie szybszy , tylko krótszy); ale to niezwykle rzadkie, a korzyść po prostu nie jest warta kosztów.((((()))))
zamiast 5, prawda? Pięć to tylko jedna konkretna liczba, co jest zbyt ograniczające - zestawy są znacznie bardziej ogólne: Pnand
implementuje wszystkie bramki, dlatego domyślnienand
może implementować wszystkie inne instrukcje. Następnie, jeśli programista manand
dostępną instrukcję, może wymyślić własne instrukcje podczas myślenia w bramkach logicznych. Od samego początku miałem na myśli to, że jeśli jest tak fundamentalny, dlaczego nie otrzymał własnej instrukcji (czyli kodu operacyjnego w logice dekodera), więc programista może użyć takiej instrukcji. Oczywiście po otrzymaniu odpowiedzi wiem, że zależy to od użytkowania oprogramowania.Na poziomie sprzętowym nand lub nor jest podstawową operacją logiczną. W zależności od technologii (lub w zależności od tego, co arbitralnie nazywasz 1 i co nazywasz 0), zarówno nand, jak i n, można zaimplementować w bardzo prosty, podstawowy sposób.
Jeśli zignorujemy przypadek „ani”, cała logika zostanie skonstruowana z nand. Ale nie dlatego, że istnieje jakiś informatyczny dowód na to, że wszystkie operacje logiczne mogą być konstruowane z - i dlatego, że po prostu nie ma żadnej elementarnej metody budowania xor, itp., Która byłaby lepsza niż konstruowanie z nandów.
W przypadku instrukcji komputerowych sytuacja jest inna. Instrukcja nand mogłaby zostać zaimplementowana i byłaby nieco tańsza niż na przykład implementacja xor. Ale tylko niewielka część, ponieważ logika, która oblicza wynik, jest niewielka w porównaniu z logiką, która dekoduje instrukcję, przesuwa operandy, upewnia się, że tylko jedna operacja jest obliczona, i zbiera wynik i dostarcza go we właściwe miejsce. Każda instrukcja wykonuje jeden cykl, tak samo jak dodawanie, które jest dziesięć razy bardziej skomplikowane logicznie. Oszczędności nand vs. xor byłyby znikome.
Liczy się wtedy, ile instrukcji jest potrzebnych do operacji faktycznie wykonywanych przez typowy kod . Nand nie jest nigdzie na górze listy najczęściej żądanych operacji. Jest o wiele bardziej powszechne, że i, lub, nie są wymagane. Projektanci procesorów i zestawów instrukcji zbadają wiele istniejących kodów i ustalą, w jaki sposób różne instrukcje wpłyną na ten kod. Najprawdopodobniej stwierdzili, że dodanie instrukcji nand doprowadziłoby do bardzo niewielkiego zmniejszenia liczby instrukcji procesora wykonujących typowy kod, a zastąpienie niektórych istniejących instrukcji nand zwiększyłoby liczbę wykonywanych instrukcji.
źródło
Tylko dlatego, że NAND (lub NOR) może implementować wszystkie bramki w logice kombinacyjnej, nie przekłada się to na wydajnego operatora bitowego w ten sam sposób. Aby zaimplementować operację AND za pomocą operacji NAND, gdzie c = a AND b, musisz mieć c = a NAND b, następnie b = -1, a następnie c = c NAND b (dla NOT). Podstawowymi logicznymi operacjami bitowymi są AND, OR, EOR, NOT, NAND i NEOR. To nie jest wiele do omówienia, a pierwsze cztery są generalnie wbudowane. W logice kombinacyjnej podstawowe obwody logiczne są ograniczone tylko liczbą dostępnych bramek, co jest zupełnie inną grą w piłkę. Liczba możliwych połączeń w programowalnej tablicy bramek, która brzmi jak to, czego naprawdę szukasz, byłaby naprawdę bardzo duża. Niektóre procesory rzeczywiście mają wbudowane tablice bramek.
źródło
Nie wdrażasz bramki logicznej tylko dlatego, że ma ona funkcjonalną kompletność, zwłaszcza jeśli inne bramki logiczne są dostępne natywnie. Wdrażasz to, co jest najczęściej używane przez kompilatory.
NAND, NOR i XNOR są bardzo rzadko potrzebne. Oprócz klasycznych operatorów bitowych AND, OR i XOR, tylko ANDN (
~a & b
) - który nie jest NAND (~(a & b)
) - miałby praktyczną użyteczność. Jeśli tak, procesor powinien to zaimplementować (i rzeczywiście niektóre procesory implementują ANDN).Aby wyjaśnić praktyczną użyteczność ANDN, wyobraź sobie, że masz maskę bitową, która używa wielu bitów, ale interesują Cię tylko niektóre z nich, które są następujące:
Zwykle chcesz sprawdzić, czy interesuje Cię maska bitowa
Zacznijmy od zebrania swoich interesujących elementów:
1. Wszystkie bity zainteresowania są ustawione: bitowe ANDN + logiczne NIE
Powiedzmy, że chcesz wiedzieć, czy wszystkie interesujące Cię elementy są ustawione. Możesz to zobaczyć jak
(my_bitmask & IT_IS_FRIDAY) && (my_bitmask & IT_IS_WARM) && (my_bitmask & THE_SUN_SHINES)
. Jednak normalnie byś to zwinął2. Ustawiony jest co najmniej jeden bit zainteresowania: bitowe ORAZ
Powiedzmy teraz, że chcesz wiedzieć, czy ustawiony jest przynajmniej jeden interesujący element. Możesz to zobaczyć jako
(my_bitmask & IT_IS_FRIDAY) || (my_bitmask & IT_IS_WARM) || (my_bitmask & THE_SUN_SHINES)
. Jednak normalnie byś to zwinął3. Co najmniej jeden bit zainteresowania nie jest ustawiony: bitowe ANDN
Powiedzmy teraz, że chcesz wiedzieć, czy nie ustawiono co najmniej jednego zainteresowania . Możesz to zobaczyć jako
!(my_bitmask & IT_IS_FRIDAY) || !(my_bitmask & IT_IS_WARM) || !(my_bitmask & THE_SUN_SHINES)
. Jednak normalnie byś to zwinął4. Nie ustawiono żadnego zainteresowania: bitowe ORAZ + logiczne NIE
Powiedzmy teraz, że chcesz wiedzieć, czy wszystkie interesujące elementy nie są ustawione. Możesz to zobaczyć jako
!(my_bitmask & IT_IS_FRIDAY) && !(my_bitmask & IT_IS_WARM) && !(my_bitmask & THE_SUN_SHINES)
. Jednak normalnie byś to zwinąłSą to typowe operacje wykonywane na masce bitowej oraz klasyczne bitowe OR i XOR. Sądzę jednak, że język (co nie jest CPU ) powinien zawierać bitowe NAND, NOR i operatorzy XNOR (których symbole byłoby
~&
,~|
i~^
), mimo rzadko stosowane. Nie dołączałbym jednak operatora ANDN w języku, ponieważ nie jest on przemienny (a ANDN b
to nie to samo, cob ANDN a
) - lepiej pisać~a & b
zamiasta ANDN b
, ten pierwszy pokazuje jaśniej asymetrię operacji.źródło