Pourquoi memcmp(a, b, size)
est-il tellement plus rapide que:
for(i = 0; i < nelements; i++) {
if a[i] != b[i] return 0;
}
return 1;
Est-ce que memcmp est une instruction de la CPU? Cela doit être assez profond car j'ai eu une accélération massive en utilisant memcmp
sur la boucle.
memcmp
est souvent implémenté dans Assembly pour tirer parti d'un certain nombre de fonctionnalités spécifiques à l'architecture, ce qui peut le rendre beaucoup plus rapide qu'une simple boucle en C.
GCC supporte memcmp
(ainsi qu'une tonne d'autres fonctions) sous la forme builtins . Dans certaines versions/configurations de GCC, un appel à memcmp
sera reconnu comme __builtin_memcmp
. Au lieu d’émettre une call
à la fonction de bibliothèque memcmp
, GCC émettra une poignée d’instructions qui serviront de version intégrée optimisée de la fonction.
Sur x86, cela exploite l’utilisation de l’instruction cmpsb
, qui compare une chaîne d’octets d’un emplacement mémoire à un autre. Ceci est couplé avec le préfixe repe
, ainsi les chaînes sont comparées jusqu'à ce qu'elles ne soient plus égales ou qu'un compte soit épuisé. (Exactement ce que memcmp
fait).
Étant donné le code suivant:
int test(const void* s1, const void* s2, int count)
{
return memcmp(s1, s2, count) == 0;
}
gcc version 3.4.4
sur Cygwin génère l'assembly suivant:
; (prologue)
mov esi, [ebp+arg_0] ; Move first pointer to esi
mov edi, [ebp+arg_4] ; Move second pointer to edi
mov ecx, [ebp+arg_8] ; Move length to ecx
cld ; Clear DF, the direction flag, so comparisons happen
; at increasing addresses
cmp ecx, ecx ; Special case: If length parameter to memcmp is
; zero, don't compare any bytes.
repe cmpsb ; Compare bytes at DS:ESI and ES:EDI, setting flags
; Repeat this while equal ZF is set
setz al ; Set al (return value) to 1 if ZF is still set
; (all bytes were equal).
; (epilogue)
Référence:
Des versions hautement optimisées de memcmp
existent dans de nombreuses bibliothèques C standard. Celles-ci s'appuient généralement sur des instructions spécifiques à l'architecture pour travailler avec de nombreuses données en parallèle.
Dans Glibc, certaines versions de memcmp
for x86_64 peuvent tirer parti des extensions de jeu d'instructions suivantes:
sysdeps/x86_64/memcmp.S
sysdeps/x86_64/multiarch/memcmp-sse4.S
sysdeps/x86_64/multiarch/memcmp-ssse3.S
La partie intéressante est que glibc détectera (au moment de l'exécution) le dernier jeu d'instructions de votre CPU et exécutera la version optimisée pour ce dernier. Voir cet extrait de sysdeps/x86_64/multiarch/memcmp.S
:
ENTRY(memcmp)
.type memcmp, @gnu_indirect_function
LOAD_RTLD_GLOBAL_RO_RDX
HAS_CPU_FEATURE (SSSE3)
jnz 2f
leaq __memcmp_sse2(%rip), %rax
ret
2: HAS_CPU_FEATURE (SSE4_1)
jz 3f
leaq __memcmp_sse4_1(%rip), %rax
ret
3: leaq __memcmp_ssse3(%rip), %rax
ret
END(memcmp)
Linux ne semble pas avoir une version optimisée de memcmp
pour x86_64, mais il en a pour memcpy
, dans Arch/x86/lib/memcpy_64.S
. Notez que cela utilise alternatives infrastructure ( Arch/x86/kernel/alternative.c
) non seulement pour décider au moment de l’exécution de la version à utiliser, mais en réalité se patcher lui-même pour ne prendre cette décision qu’au démarrage. up.
Est-ce que memcmp est une instruction de la CPU?
C'est au moins une fonction intrinsèque très optimisée fournie par le compilateur. Peut-être une seule instruction machine, ou deux, selon la plate-forme, que vous n'avez pas spécifiée.
Il s’agit généralement d’un compilateur intrinsèque traduit en assembleur rapide avec des instructions spécifiques permettant de comparer des blocs de mémoire.