Modern işlemcilerde neden “hiçbir talimat” yoktur?


52

Neden x86 tasarımcıları (veya diğer CPU mimarileri) dahil etmemeye karar verdiler? Diğer mantık kapılarını inşa etmek için kullanılabilecek bir mantık geçididir, dolayısıyla tek bir komut olarak hızlıdır. Zincirleme notve andtalimatlar yerine (her ikisi de yaratılmıştır nand), neden nandtalimat yok ?


20
Nand talimatı için hangi kullanım alanınız var? Muhtemelen x86 tasarımcıları hiçbir zaman bulamadı
PlasmaHH

16
ARM olan BICtalimatı vardır a & ~b. Kol başparmak-2 olan ORNtalimatı vardır ~(a | b). ARM oldukça modern. CPU talimat setindeki bir talimatı kodlamanın maliyeti vardır. Yani sadece en "faydalı" olanlar ISA'ya giriyorlar.
Eugene Sh.

24
@Amumu Biz de ~(((a << 1) | (b >> 1)) | 0x55555555)talimat verebilirdik . Amaç ~(((a << 1) | (b >> 1)) | 0x55555555), 6 yerine tek bir öğretiye çevrilebilecek şekilde olacaktır. Öyleyse neden olmasın?
user253751

11
@Amumu: Bu bir usecase değil ve aynı zamanda ~ değil! Bir usecase, bu öğretimin neden faydalı olduğunu ve nerede uygulanabileceğini belirleyici bir nedendir. Akıl yürütme, "Talimat orada kullanılmalı, kullanılsın" demeye benziyor, ancak soru, "bunun için ne kullanılması, kaynak harcamak için çok faydalı olması".
PlazmaHH

4
45 yıldır programlama yapıyorum, birkaç derleyici yazdım ve IMP gibi uygun olduğunda bazı tuhaf mantıksal operatörler kullandım, ancak hiçbir zaman bir NAND operatörü veya talimatı kullanmamıştım.
user207421

Yanıtlar:


62

http://www.ibm.com/support/knowledgecenter/ssw_aix_61/com.ibm.aix.alangref/idalangref_nand_nd_instrs.htm : GÜÇ NAND değerine sahiptir.

Ancak, genellikle modern CPU'lar otomatik kod oluşturma işlemlerini derleyiciler tarafından eşleştirilmek üzere oluşturulmuştur ve bit yönünde NAND çok nadiren çağrılır. Bitsel ve VE VEYA veri yapılarındaki bit alanlarını değiştirmek için daha sık kullanılır. Aslında, SSE, AND-NOT'a sahip değil, NAND'a sahip.

Her komutun kod çözme mantığında bir maliyeti vardır ve başka bir şey için kullanılabilecek bir opcode kullanır. Özellikle x86 gibi değişken uzunluktaki kodlamalarda, kısa kodların bitmesine neden olabilir ve daha uzun olanları kullanmak zorunda kalırsınız; bu durum tüm kodu yavaşlatır.


5
@supercat AND-NOT, bit-set değişkeninde bitleri kapatmak için yaygın olarak kullanılır. egif(windowType & ~WINDOW_RESIZABLE) { ... do stuff for variable-sized windows ... }
adib

2
@ adib: Yup. "Ve-değil" in ilginç bir özelliği, "bitsel değil" operatörünün aksine [~] sonuç boyutunun önemli olmamasıdır. Eğer foobir uint64_t olduğunu beyan foo &= ~something;bazen olması gerekenden fazla bit temizlemek olabilir, ama bir olsaydı &~=operatörü böyle sorunlar önlenebilir.
supercat,

6
Eğer WINDOW_RESIZABLEbir sabitse, @adib , o zaman bir optimizer ~WINDOW_RESIZABLEderleme zamanında değerlendirmelidir , bu yüzden bu sadece çalışma anında bir AND olur.
alephzero

4
@MarkRansom: Hayır, sebep ve sonuç hesaplama geçmişinden tamamen doğru. İnsan montaj programcıları yerine derleyiciler için optimize edilmiş CPU tasarlama olgusu, RISC hareketinin bir parçasıydı (RISC hareketinin kendisi sadece bu yönden daha geniştir). Derleyiciler için tasarlanan CPU'lar arasında ARM ve Atmel AVR bulunur. 90'ların sonunda ve
00'ların

3
Bu günlerde kayıt yaptırma işlemleri RAM erişimiyle karşılaştırıldığında esasen ücretsizdir. Yedekli talimatların uygulanması, CPU'daki silikon taşınmaz mallarına neden olur. Bu nedenle, genellikle sadece bir bit biti-VEYA biti biti biçimi olacaktır - çünkü bitsel bir tamamlayıcı kayıt-kayıt işlemi eklemek hiçbir şeyi yavaşlatmaz.
nigel222

31

Böyle bir ALU fonksiyonunun maliyeti

1) işlevin kendisini gerçekleştiren mantık

2) bu fonksiyon sonucunu seçen seçici, tüm ALU fonksiyonlarının dışında kalanlar yerine

3) bu seçeneğin komut setinde bulunma maliyeti (ve başka bir yararlı fonksiyona sahip olmamak)

Size katılıyorum 1) maliyeti çok küçük. Ancak 2) ve 3) maliyeti, işlevden neredeyse bağımsızdır. Bu durumda, 3) maliyetin (talimatta geçen bitler) bu spesifik talimatın olmamasının nedeni olduğunu düşünüyorum. Bir talimattaki bitler, bir CPU / mimari tasarımcısı için çok az kaynaktır.


29

Arkanı dön - ilk olarak Nand'ın donanım mantığı tasarımında neden popüler olduğunu görün - orada birçok yararlı özelliği var. O zaman bu özelliklerin hala bir CPU komutunda geçerli olup olmadığını sorun ...

TL / DR - değil, bu yüzden yerine And, Or veya Not kullanmanın bir dezavantajı yok.

Kablolu Nand mantığına en büyük avantaj, devrenin giriş ve çıkışları arasındaki mantık seviyelerinin (transistör aşamaları) azaltılmasıyla kazanılan hızdı. Bir CPU'da, saat hızı, toplama gibi çok daha karmaşık işlemlerin hızıyla belirlenir, bu nedenle bir VE işleminin hızlandırılması saat hızını artırmanıza izin vermez.

Ve diğer talimatları bir araya getirmeniz için gereken sayı kaybolmak üzere küçüktür - Nand gerçekten talimat setindeki alanını kazanmaz.


1
Giriş izolasyonunun gerekli olmadığı durumlarda, "ve değil" donanım açısından oldukça ucuz görünmektedir. 1977'de, bir "XOR" işlevini gerçekleştirmek için ışık başına iki transistör ve iki diyot kullanan ebeveynimin römorku için bir dönüş sinyali kontrolörü tasarladım [sol lamba == xor (sol sinyal, fren); sağ lamba == xor (sağ sinyal, fren)], her ışık için esasen iki veya hiç işlevsiz kablolama veya kullanma. LSI tasarımında bu tür hileler görmedim, ancak TTL veya NMOS'ta, bir girişi besleyen her ne olursa olsun, yeterli sürüş kabiliyetine sahip olabilecek durumlarda, bu hileler devreleri koruyabileceğini düşünürdüm.
supercat

12

Burada Brian ve Wouter ve pjc50 ile aynı fikirdeyim.

Ayrıca, genel amaçlı olarak, özellikle CISC, işlemciler, talimatların hepsinde aynı verim bulunmadığını da eklemek isterim - karmaşık bir işlem basit bir işlemden daha fazla döngü alabilir.

X86'yı göz önünde bulundurun: AND("ve" olan bir işlemdir) muhtemelen çok hızlıdır. Aynı şey için de geçerli NOT. Biraz sökmeye bakalım:

Giriş kodu:

#include <immintrin.h>
#include <stdint.h>

__m512i nand512(__m512i a, __m512i b){return ~(a&b);}
__m256i nand256(__m256i a, __m256i b){return ~(a&b);}
__m128i nand128(__m128i a, __m128i b){return ~(a&b);}
uint64_t nand64(uint64_t a, uint64_t b){return ~(a&b);}
uint32_t nand32(uint32_t a, uint32_t b){return ~(a&b);}
uint16_t nand16(uint16_t a, uint16_t b){return ~(a&b);}
uint8_t nand8(uint8_t a, uint8_t b){return ~(a&b);}

Montaj üretme komutu:

gcc -O3 -c -S  -mavx512f test.c

Çıkış Meclisi (kısaltılmış):

    .file   "test.c"
nand512:
.LFB4591:
    .cfi_startproc
    vpandq  %zmm1, %zmm0, %zmm0
    vpternlogd  $0xFF, %zmm1, %zmm1, %zmm1
    vpxorq  %zmm1, %zmm0, %zmm0
    ret
    .cfi_endproc
nand256:
.LFB4592:
    .cfi_startproc
    vpand   %ymm1, %ymm0, %ymm0
    vpcmpeqd    %ymm1, %ymm1, %ymm1
    vpxor   %ymm1, %ymm0, %ymm0
    ret
    .cfi_endproc
nand128:
.LFB4593:
    .cfi_startproc
    vpand   %xmm1, %xmm0, %xmm0
    vpcmpeqd    %xmm1, %xmm1, %xmm1
    vpxor   %xmm1, %xmm0, %xmm0
    ret
    .cfi_endproc
nand64:
.LFB4594:
    .cfi_startproc
    movq    %rdi, %rax
    andq    %rsi, %rax
    notq    %rax
    ret
    .cfi_endproc
nand32:
.LFB4595:
    .cfi_startproc
    movl    %edi, %eax
    andl    %esi, %eax
    notl    %eax
    ret
    .cfi_endproc
nand16:
.LFB4596:
    .cfi_startproc
    andl    %esi, %edi
    movl    %edi, %eax
    notl    %eax
    ret
    .cfi_endproc
nand8:
.LFB4597:
    .cfi_startproc
    andl    %esi, %edi
    movl    %edi, %eax
    notl    %eax
    ret
    .cfi_endproc

Gördüğünüz gibi, 64 alt boyuttaki veri türleri için, işler sadece derleyicimin "yerel" bit genişliği gibi göründüğü kadar uzun sürüyor (dolayısıyla ve l ve l değil ).

Arada var olduğu gerçeği mov, yalnızca eaxbir işlevin dönüş değerini içeren register olduğu gerçeğinden kaynaklanmaktadır . Normalde, edisonucu hesaplamak için genel amaçlı kayıt defterinde hesaplamanız gerekir.

64 bit için, aynı - sadece "quad" (dolayısıyla, sonunda q) kelimeleriyle ve rax/ rsiyerine eax/ ile edi.

Görünüşe göre 128 bit operand ve daha fazlası için Intel bir "değil" işlemi gerçekleştirmeyi umursamadı; bunun yerine, derleyici bir all- 1register üretir (yazmacın kendisiyle karşılaştırılması, kayıtlarda vdcmpeqdtalimat ile saklanan sonuç ), ve xors.

Kısacası: Birden fazla temel talimatla karmaşık bir işlem uygulayarak, işlemi yavaşlatmanız gerekmez - daha hızlı değilse, birden fazla talimatın işini yapan tek bir talimatın olması hiçbir avantajı yoktur.


10

İlk önce bitsel ve mantıksal işlemleri birbirine karıştırmayın.

Bitsel işlemler genellikle bit alanlarındaki bitleri ayarlamak / silmek / değiştirmek / kontrol etmek için kullanılır. Bu işlemlerin hiçbiri nand gerektirmez ("ve değil", ayrıca "biraz açık" olarak da bilinir).

Çoğu modern programlama dilinde mantıksal işlemler, kısa devre mantığı kullanılarak değerlendirilir. Bu yüzden genellikle bunları uygulamak için branş temelli bir yaklaşım gereklidir. Derleyici, kısa devre ile tam değerlendirmenin program davranışında bir fark yaratmadığını belirleyebilse bile, mantıksal işlemler için işlenenler, genellikle bitsel biçim işlemleri kullanarak ifadeyi uygulamak için uygun bir biçimde değildir.


10

NAND genellikle doğrudan uygulanmaz, çünkü AND komutunu açık bir şekilde NAND durumuna atlamanızı sağlar.

Bir CPU'da mantıksal bir işlem gerçekleştirmek genellikle bir bayrak kaydındaki bitleri ayarlar.

Bayrak kayıtlarının çoğunda SIFIR bayrağı vardır. Mantıksal bir işlemin sonucu sıfır ise sıfır bayrağı ayarlanır, aksi takdirde silinir.

Çoğu modern CPU, sıfır bayrağı ayarlanmışsa atlayan bir atlama komutuna sahiptir. Ayrıca sıfır bayrağı ayarlanmadığında atlayan bir yönergeleri vardır.

AND ve NAND tamamlayıcıdır. Bir AND işleminin sonucu sıfır ise, NAND işleminin sonucu 1'dir ve bunun tersi de geçerlidir.

Öyleyse eğer iki değerin NANDI doğruysa, zıplama istiyorsanız, o zaman sadece AND işlemini yapın ve sıfır bayrağı ayarlanmışsa zıplayın.

Öyleyse eğer iki değerin NAND değeri yanlıştırsa atlamak istiyorsanız, sadece AND işlemini yapın ve sıfır bayrağı açıksa atlayın.


Nitekim - şartlı atlama talimatı seçimi, her bir işlem için bireysel olarak bu seçeneği uygulamak zorunda kalmadan, bütün bir işlem sınıfı için ters çevirme ve ters çevirme mantığı seçeneği sunar.
Chris Stratton,

Bu en iyi cevap olmalıydı. Sıfır bayrak işlemleri, NAND'ı AND + JNZ ve AND + JZ'nin esasen kısa devre / mantıksal olması nedeniyle hem mantıksal işlemler için gereksiz kılar, hem de aynı sayıda opcode alır.
Yalan Ryan

4

Bir şeyin ucuz olması, maliyet etkin olduğu anlamına gelmez .

Argümanınızı ve reklamınızı almazsak, bir işlemcinin çoğunlukla yüzlerce NOP tadımıdan oluşması gerektiği sonucuna varacağız - çünkü bunların uygulanması en ucuzları.

Ya da finansal enstrümanlarla karşılaştırın: sadece yapabileceğiniz için% 0.01 getiri ile 1 $ 'lık tahvil alır mısınız? Hayır, daha iyi bir getiri ile 10 $ 'lık tahvil almak için yeterli olana kadar bu dolarları tasarruf etmeyi tercih edersiniz. Aynı şey, bir CPU'daki silikon bütçesine de bağlı: NAND gibi birçok ucuz ancak işe yaramaz op'ları fırlatmak ve kaydedilen transistörleri daha pahalı ama gerçekten yararlı bir şeye koymak etkili.

Mümkün olduğunca çok ops olmak için herhangi bir yarış yok. RISC'e karşı CISC, Turing'in en başından beri bildiklerini kanıtladı: daha az daha fazla. Aslında mümkün olduğunca az ops yapmak daha iyidir.


nopDiğer tüm mantık kapıları uygulayamaz, ama nandya noretkin yazılımda bir CPU uygulanan herhangi bir talimata yeniden oluşturabilirsiniz. RISC yaklaşımını ele alırsak, bu ..
Amumu

@Amumu Karıştırdığınızı gateve instruction. Gates, diğer yollarda değil, talimatları uygulamak için kullanılır. NOPbir talimattır, bir geçit değil. Ve evet, CPU'lar tüm talimatları uygulamak için binlerce, hatta milyonlarca NAND geçidi içerir. Sadece "NAND" talimatı değil.
Ajan_L

2
@Amumu Bu RISC yaklaşımı değil :) Bu, çok özel uygulamaların dışında pek kullanışlı olmayan "en geniş soyutlamaları kullanın" yaklaşımıdır. Tabii, nanddiğer geçitleri uygulamak için kullanılabilecek bir geçit; ama zaten diğer tüm talimatlara sahipsin . Bir nandtalimat kullanarak onları yeniden uygulamak daha yavaş olacaktır . Ve daha sık kullanılan (örneğin, nanddaha hızlı kod değil , sadece kısa kod) kiraz seçtikleri özel örneklerin aksine, bunu tolere etmek için çok sık kullanılırlar ; Ancak bu oldukça nadirdir ve fayda sadece maliyete değmez.
Luaan

@Amumu Eğer yaklaşımınızı kullanırsak, pozisyon numaralarımız olmazdı. ((((()))))5 yerine basitçe söyleyebileceğin nokta ne ? Beşi yalnızca belirli bir sayı, bu da çok sınırlayıcı - setler çok daha genel: P
Luaan

@Agent_L Evet, kapıların talimatları uyguladığını biliyorum. nandtüm geçitleri uygular, bu nedenle dolaylı olarak nanddiğer tüm talimatları uygulayabilir. Daha sonra, eğer bir programcının bir nandtalimatı varsa, mantık geçitlerini düşünürken kendi talimatlarını icat edebilir. En baştan demek istediğim, eğer bu çok temelse, neden kendi talimatı verilmediği (kod çözücü mantığında bir opcode) olduğu, yani bir programcının böyle bir talimatı kullanabilmesidir. Tabii ki cevaplandıktan sonra, yazılım kullanımına bağlı olduğunu biliyorum.
Amumu

3

Bir donanım seviyesinde, ya da ne de ne temel mantık işlemidir. Teknolojiye bağlı olarak (veya ne keyfi bir şekilde aradığınıza ve 0 olarak adlandırdığınıza bağlı olarak), nand ya da çok basit, basit bir şekilde uygulanabilir.

Eğer "ne" davasını görmezden gelirsek, diğer tüm mantık nand'den oluşturulur. Tüm mantık işlemleri inşa edilebileceği bazı bilgisayar bilimi kanıt yoktur çünkü Ama - nedeni sadece orada olduğunu değil NAND en onu inşa ardından iyi olduğunu vb xor oluşturmak için herhangi bir temel yöntem veya, vb.

Bilgisayar talimatları için durum farklıdır. Bir nand talimatı uygulanabilirdi ve örneğin xor'u uygulamaktan daha ucuz olacaktı. Ancak sadece küçük bir bit, çünkü sonucu hesaplayan mantık, talimatı çözen mantığa kıyasla küçük olduğundan, işlenenleri hareket ettirir, bir işlemin yalnızca hesaplandığından emin olur ve sonucu alır ve doğru yere sunar. Her komutun yürütülmesi bir döngü sürer, mantık açısından on kat daha karmaşık olan bir ekleme gibi. Nand ve xor değerlerinde tasarruf ihmal edilebilir.

O zaman önemli olan tipik kodla gerçekleştirilen işlemler için kaç tane talimatın gerekli olduğudur . Nand, sıkça istenen işlemler listesinin başında hiçbir yere yakın değildir. Bu çok daha yaygındır ve talep edilmez. İşlemci ve komut seti tasarımcıları birçok mevcut kodu inceleyecek ve farklı talimatların bu kodu nasıl etkileyeceğini belirleyecektir. Muhtemelen bir nand komutunun eklenmesinin, tipik kodu çalıştırmak için yürütülen işlemci talimatlarının sayısında çok az bir azalmaya yol açabileceğini ve mevcut bazı talimatların nand ile değiştirilmesinin gerçekleştirilen talimat sayısını arttıracağını bulmuşlardır.


2

NAND'ın (veya NOR) tüm geçitleri birleşimsel mantığa uygulayabilmesi, aynı şekilde verimli bir bitsel operatöre çevirmez. Sadece NAND işlemlerini kullanarak bir AND uygulamak için, burada c = a AND b, c = a NAND b, sonra b = -1, sonra c = c NAND b (bir NOT için) olmalıdır. Temel mantık bitsel işlemler AND, VEYA, EOR, DEĞİL, NAND ve NEOR'dur. Bu, örtülecek pek bir şey değil ve ilk dördü zaten zaten inşa edildi. Birleşimsel mantıkta, temel mantık devreleri, tamamen farklı bir top oyunu olan mevcut kapı sayısı ile sınırlıdır. Gerçekten sonra olduklarınız gibi ses çıkaran, programlanabilir bir kapı dizisindeki olası bağlantıların sayısı gerçekten çok büyük bir sayı olacaktır. Bazı işlemciler aslında yerleşik bir kapı dizisine sahiptir.


0

Bir mantık geçidini uygulamıyorsunuz, çünkü işlevsel bütünlüğü var, özellikle de diğer mantık kapıları yerel olarak mevcutsa. Derleyiciler tarafından en çok kullanılanları uygularsınız.

NAND, NOR ve XNOR çok nadiren gereklidir. Klasik bitsel operatörleri AND, OR ve XOR'un yanı sıra, yalnızca ~a & bNAND ( ~(a & b)) olmayan ANDN ( ) pratik bir yardımcı programa sahip olacaktır. Varsa, bir CPU bunu uygulamalıdır (ve gerçekten bazı CPU'lar ANDN'yi uygular).

ANDN'nin pratik faydasını açıklamak için, birçok bit kullanan bir bit maskesinin olduğunu hayal edin, ancak yalnızca aşağıdakilerden bazılarıyla ilgileniyorsunuz:

enum my_flags {
    IT_IS_FRIDAY = 1,
    ...
    IT_IS_WARM = 8,
    ...
    THE_SUN_SHINES = 64,
    ...
};

Normalde bit maskesiyle ilgilendiğiniz kısımları kontrol etmek ister misiniz

  1. Hepsi ayarlandı
  2. En az bir tane ayarlandı
  3. En az bir ayarlanmamış
  4. Hiçbiri ayarlanmadı

İlgi alanlarınızı bir araya getirerek başlayalım:

#define BITS_OF_INTEREST (IT_IS_FRIDAY | IT_IS_WARM | THE_SUN_SHINES)

1. Tüm ilgi bitleri belirlenir: bit yönünde ANDN + mantıksal DEĞİL

Diyelim ki ilgi alanlarınızın tamamen belirlenmiş olup olmadığını bilmek istiyorsunuz. Gibi görebilirsiniz (my_bitmask & IT_IS_FRIDAY) && (my_bitmask & IT_IS_WARM) && (my_bitmask & THE_SUN_SHINES). Ancak normalde bunu daraltabilirsin

unsigned int life_is_beautiful = !(~my_bitmask & BITS_OF_INTEREST);

2. En az bir tane ilgi alanı belirlenmiş: bit yönünde VE

Şimdi en az bir ilgi alanı ayarlanmış olup olmadığını bilmek istediğinizi varsayalım. Olarak görebilirsiniz (my_bitmask & IT_IS_FRIDAY) || (my_bitmask & IT_IS_WARM) || (my_bitmask & THE_SUN_SHINES). Ancak normalde bunu daraltabilirsin

unsigned int life_is_not_bad = my_bitmask & BITS_OF_INTEREST;

İlgi 3. En az bir bit olduğunu değil ayarlayın: bitsel ANDN

Şimdi senin ilgi en az bir bit olup olmadığını bilmek istediğinizi varsayalım değil ayarlayın. Olarak görebilirsiniz !(my_bitmask & IT_IS_FRIDAY) || !(my_bitmask & IT_IS_WARM) || !(my_bitmask & THE_SUN_SHINES). Ancak normalde bunu daraltabilirsin

unsigned int life_is_imperfect = ~my_bitmask & BITS_OF_INTEREST;

4. İlgi alanı belirlenmemiş: bit yönünde VE + mantıksal DEĞİL

Şimdi senin ilgi tüm bitleri eğer bilmek istediğinizi varsayalım değil ayarlayın. Olarak görebilirsiniz !(my_bitmask & IT_IS_FRIDAY) && !(my_bitmask & IT_IS_WARM) && !(my_bitmask & THE_SUN_SHINES). Ancak normalde bunu daraltabilirsin

unsigned int life_is_horrible = !(my_bitmask & BITS_OF_INTEREST);

Bunlar bir bit maskesi üzerinde yapılan genel işlemlerin yanı sıra klasik bit yönünde OR ve XOR'dur. O olsa sizce bir dil (değil bir CPU ) bitsel NAND içermelidir, NOR ve XNOR (kimin sembolleri olacaktır operatörler ~&, ~|ve ~^) rağmen nadiren kullanılan. O değişmeli olmadığından ben, gerçi bir dilde ANDN operatörü dahil olmaz ( a ANDN baynı değildir b ANDN a) - Daha iyi yazmak için ~a & byerine a ANDN boperasyon, eski gösterileri daha net asimmetry.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.