Où est la mise en œuvre de strlen() dans le GCC?
strlen()
est effectivement mis en œuvre.
10 réponses
Vous devriez chercher dans glibc, pas GCC -- il semble être défini dans strlen.c
-- voici un lien vers strlen.c pour glibc version 2.7... Et voici un lien vers le glibc dépôt SVN en ligne pour strlen.c.
La raison pour laquelle vous devriez être à la recherche à glibc et pas gcc est:
la bibliothèque GNU C est utilisée comme bibliothèque C dans le système GNU et la plupart des systèmes avec Linux noyau.
je me rends compte que cette question Est vieille de 4 ans, mais gcc inclura souvent son copie de strlen si vous n'avez pas #include <string.h>
et aucune des réponses (y compris la réponse acceptée) n'en tient compte. Si vous oubliez, vous obtiendrez un avertissement:
file_name:line_number: warning: incompatible implicit declaration of built-in function 'strlen'
et gcc inline sa copie qui sur x86 est la variante de repnz scasb asm à moins que vous ne passiez-Werror ou-FNO-builtin. Les fichiers liés à ce sont en gcc/config/<platform>/<platform>.{c,md}
Il est également contrôlé par gcc/les builtins.C. Dans le cas où vous vous demandiez si et comment un strlen() a été optimisé à une constante, voyez la fonction définie comme tree c_strlen(tree src, int only_value)
dans ce fichier. Il contrôle également la façon dont strlen (entre autres) est étendu et plié (basé sur la config/plate-forme mentionnée précédemment)
Voici le bsd mise en oeuvre
size_t
strlen(const char *str)
{
const char *s;
for (s = str; *s; ++s)
;
return (s - str);
}
Est-ce ce que vous recherchez? strlen () source. Voir le git pour plus d'informations. glibc ressources page a des liens vers les dépôts git si vous voulez attraper plutôt que de regarder le vue web.
bien que l'affiche originale ne le sache pas ou ne le cherche pas, gcc inline en interne un certain nombre de fonctions dites "builtin" c qu'il définit lui-même, y compris certaines des fonctions mem*() et (selon la version gcc) strlen. Dans de tels cas, la version de la bibliothèque n'est essentiellement jamais utilisée, et pointer la personne vers la version dans glibc n'est pas strictement correct. (Il le fait pour des raisons de performance-en plus de l'amélioration lui-même produit, gcc "sait" certaines choses sur les fonctions quand il les fournit, comme, par exemple, que strlen est une fonction pure et qu'il peut ainsi optimiser les appels multiples, ou dans le cas des fonctions mem*() qu'aucun aliasing n'a lieu.)
pour plus d'information, voir http://gcc.gnu.org/onlinedocs/gcc/Other-Builtins.html
Google Recherche De Code est un bon point de départ pour des questions comme ça. Ils pointent généralement vers diverses sources et implémentations différentes d'une fonction.
Dans votre cas particulier: GoogleCodeSearch (strlen)
Google Code Search a été complètement arrêté le mars 2013
définie dans glibc / string / strlen.c
#include <string.h>
#include <stdlib.h>
#undef strlen
#ifndef STRLEN
# define STRLEN strlen
#endif
/* Return the length of the null-terminated string STR. Scan for
the null terminator quickly by testing four bytes at a time. */
size_t
STRLEN (const char *str)
{
const char *char_ptr;
const unsigned long int *longword_ptr;
unsigned long int longword, himagic, lomagic;
/* Handle the first few characters by reading one character at a time.
Do this until CHAR_PTR is aligned on a longword boundary. */
for (char_ptr = str; ((unsigned long int) char_ptr
& (sizeof (longword) - 1)) != 0;
++char_ptr)
if (*char_ptr == '')
return char_ptr - str;
/* All these elucidatory comments refer to 4-byte longwords,
but the theory applies equally well to 8-byte longwords. */
longword_ptr = (unsigned long int *) char_ptr;
/* Bits 31, 24, 16, and 8 of this number are zero. Call these bits
the "holes." Note that there is a hole just to the left of
each byte, with an extra at the end:
bits: 01111110 11111110 11111110 11111111
bytes: AAAAAAAA BBBBBBBB CCCCCCCC DDDDDDDD
The 1-bits make sure that carries propagate to the next 0-bit.
The 0-bits provide holes for carries to fall into. */
himagic = 0x80808080L;
lomagic = 0x01010101L;
if (sizeof (longword) > 4)
{
/* 64-bit version of the magic. */
/* Do the shift in two steps to avoid a warning if long has 32 bits. */
himagic = ((himagic << 16) << 16) | himagic;
lomagic = ((lomagic << 16) << 16) | lomagic;
}
if (sizeof (longword) > 8)
abort ();
/* Instead of the traditional loop which tests each character,
we will test a longword at a time. The tricky part is testing
if *any of the four* bytes in the longword in question are zero. */
for (;;)
{
longword = *longword_ptr++;
if (((longword - lomagic) & ~longword & himagic) != 0)
{
/* Which of the bytes was the zero? If none of them were, it was
a misfire; continue the search. */
const char *cp = (const char *) (longword_ptr - 1);
if (cp[0] == 0)
return cp - str;
if (cp[1] == 0)
return cp - str + 1;
if (cp[2] == 0)
return cp - str + 2;
if (cp[3] == 0)
return cp - str + 3;
if (sizeof (longword) > 4)
{
if (cp[4] == 0)
return cp - str + 4;
if (cp[5] == 0)
return cp - str + 5;
if (cp[6] == 0)
return cp - str + 6;
if (cp[7] == 0)
return cp - str + 7;
}
}
}
}
libc_hidden_builtin_def (strlen)
je me rends compte que c'est une vieille question, vous pouvez trouver les sources du noyau linux à github ici, et l'implémentation 32 bits pour strlen () peut être trouvée dans strlen_32.c sur github. Le fichier mentionné a cette implémentation.
#include <linux/types.h>
#include <linux/string.h>
#include <linux/module.h>
size_t strlen(const char *s)
{
/* Get an aligned pointer. */
const uintptr_t s_int = (uintptr_t) s;
const uint32_t *p = (const uint32_t *)(s_int & -4);
/* Read the first word, but force bytes before the string to be nonzero.
* This expression works because we know shift counts are taken mod 32.
*/
uint32_t v = *p | ((1 << (s_int << 3)) - 1);
uint32_t bits;
while ((bits = __insn_seqb(v, 0)) == 0)
v = *++p;
return ((const char *)p) + (__insn_ctz(bits) >> 3) - s;
}
EXPORT_SYMBOL(strlen);
Vous pouvez utiliser ce code, le plus simple le meilleur !
size_t Strlen ( const char * _str )
{
size_t i = 0;
while(_str[i++]);
return i;
}
glibc 2.26 a plusieurs implémentations d'assemblage optimisées à la main de strlen
glibc-2.26
, un rapide:
git ls-files | grep strlen.S
dans l'arbre de glibc montre une douzaine d'implémentations d'assemblage optimisées à la main pour toutes les arches principales et les variations.
En particulier, x86_64 seul a 3 variations:
sysdeps/x86_64/multiarch/strlen-avx2.S
sysdeps/x86_64/multiarch/strlen-sse2.S
sysdeps/x86_64/strlen.S
un moyen rapide et sale pour déterminer lequel est utilisé, est de déboguer par étapes un test programme:
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
int main(void) {
size_t size = 0x80000000, i, result;
char *s = malloc(size);
for (i = 0; i < size; ++i)
s[i] = 'a';
s[size - 1] = '';
result = strlen(s);
assert(result == size - 1);
return EXIT_SUCCESS;
}
compilé avec:
gcc -ggdb3 -std=c99 -O0 a.c
au Large de la chauve-souris:
disass main
contient:
callq 0x555555554590 <strlen@plt>
donc la version libc est appelée.
Après quelques si
niveau de l'instruction des mesures dans le, GDB atteint:
__strlen_avx2 () at ../sysdeps/x86_64/multiarch/strlen-avx2.S:52
52 ../sysdeps/x86_64/multiarch/strlen-avx2.S: No such file or directory.
ce qui me dit que strlen-avx2.S
a été utilisé.
Ensuite, j'ai continué à confirmer avec:
disass __strlen_avx2
et comparer le démontage avec le glibc source.
il n'est pas surprenant que la version AVX2 ait été utilisée, puisque j'ai un i7-7820HQ CPU avec date de lancement Q1 2017 et support AVX2, et AVX2 est la plus avancée des implémentations d'assemblage, avec la date de lancement Q2 2013, alors que SSE2 est beaucoup plus ancien de 2004.
C'est d'ici qu'une grande partie de la dureté de glibc vient: il y a beaucoup d'assemblage écrit à la main optimisé à l'Arc code.
testé dans Ubuntu 17.10, gcc 7.2.0, glibc 2.26.
-O3
TODO: with -O3
, gcc n'utilise pas les strlen
, il génère juste l'assemblage inline, qui est mentionné à: https://stackoverflow.com/a/19885891/895245
Est-ce qu'elle peut optimiser encore mieux? Mais sa sortie ne contient pas d'instructions AVX2, donc je pense que ce n'est pas le cas.
https://www.gnu.org/software/gcc/projects/optimize.html mentions:
Lacunes de la GCC de l'optimiseur
glibc a des versions assembleur en ligne de diverses fonctions de chaîne; GCC en a quelques-unes, mais pas nécessairement les mêmes sur les mêmes architectures. Des entrées optab supplémentaires, comme celles pour ffs et strlen, pourraient être fournies pour plusieurs autres fonctions dont memset, strchr, strcpy et strrchr.
mes tests simples montrent que le -O3
la version est en fait plus rapide, donc GCC a fait le bon choix.