Vorrei essere d'accordo con Brian qui, Wouter e PJC50.
Vorrei anche aggiungere che per scopi generici, in particolare CISC, processori, istruzioni non hanno tutti gli stessi throughput - un'operazione complicata potrebbe richiedere semplicemente più cicli di una semplice.
Considera X86: AND(che è un'operazione "e") è probabilmente molto veloce. Lo stesso vale per NOT. Diamo un'occhiata a un po 'di smontaggio:
Codice di input:
#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);}
Comando per produrre assemblaggio:
gcc -O3 -c -S -mavx512f test.c
Gruppo di uscita (abbreviato):
.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
Come si può vedere, per i tipi di dati sub-64-dimensioni, le cose sono semplicemente tutte gestite come anela (da qui il e l e non l ), dato che è il bitwidth "nativo" del mio compilatore, come sembra.
Il fatto che ci sia movuna via di mezzo è dovuto solo al fatto che eaxè il registro che contiene il valore restituito di una funzione. Normalmente, si calcola semplicemente nel ediregistro generale per calcolare con il risultato.
Per 64 bit, è lo stesso - solo con qparole "quad" (quindi, finali ) e rax/ rsiinvece di eax/ edi.
Sembra che per operandi a 128 bit e superiori, Intel non si preoccupasse di implementare un'operazione "non"; invece, il compilatore produce un 1registro completo (auto-confronto del registro con se stesso, risultato memorizzato nel registro con l' vdcmpeqdistruzione), ed xorè quello.
In breve: implementando un'operazione complicata con più istruzioni elementari, non devi necessariamente rallentare l'operazione - semplicemente non c'è alcun vantaggio ad avere un'istruzione che fa il lavoro di più istruzioni se non è più veloce.