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 mov
una via di mezzo è dovuto solo al fatto che eax
è il registro che contiene il valore restituito di una funzione. Normalmente, si calcola semplicemente nel edi
registro generale per calcolare con il risultato.
Per 64 bit, è lo stesso - solo con q
parole "quad" (quindi, finali ) e rax
/ rsi
invece 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 1
registro completo (auto-confronto del registro con se stesso, risultato memorizzato nel registro con l' vdcmpeqd
istruzione), 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.