programming length ccs c glibc strlen

length - ¿Dónde está la implementación de strlen() en GCC?



strlen ccs (10)

¿Es esto lo que estás buscando? fuente strlen () Ver el repositorio git para más información. La página de recursos de glibc tiene enlaces a los repositorios de git si desea capturarlos en lugar de mirar la vista web.

¿Alguien me puede indicar la definición de strlen() en GCC? He estado preparando la versión 4.4.2 durante aproximadamente media hora (mientras hago Google como loca) y parece que no puedo encontrar dónde se implementa realmente strlen() .


Aquí está la implementación de bsd

size_t strlen(const char *str) { const char *s; for (s = str; *s; ++s) ; return (s - str); }


Aunque el póster original puede no haber sabido esto o haber estado buscando esto, gcc integra internamente una serie de funciones c "incorporadas" que define por sí misma, incluidas algunas de las funciones mem * () y (dependiendo de la versión gcc) strlen. En tales casos, la versión de la biblioteca esencialmente nunca se usa, y señalar a la persona en la versión en glibc no es estrictamente correcto. (Lo hace por razones de rendimiento: además de la mejora que produce la alineación, gcc "sabe" ciertas cosas acerca de las funciones cuando las proporciona, como, por ejemplo, que strlen es una función pura y que, por lo tanto, puede optimice las llamadas múltiples, o en el caso de las funciones mem * () que no tienen lugar alias.)

Para obtener más información sobre esto, consulte http://gcc.gnu.org/onlinedocs/gcc/Other-Builtins.html



Me doy cuenta de que esta es una vieja pregunta. Puede encontrar las fuentes del kernel de Linux en github here , y la implementación de 32 bits para strlen () se puede encontrar en strlen_32.c en github. El archivo mencionado tiene esta implementación.

#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);


Me doy cuenta de que esta pregunta tiene 4 años de antigüedad, pero gcc a menudo incluirá su propia copia de strlen si no incluye #include <string.h> y ninguna de las respuestas (incluida la respuesta aceptada) cuenta para eso. Si olvida, recibirá una advertencia:

file_name:line_number: warning: incompatible implicit declaration of built-in function ''strlen''

y gcc incluirá en línea su copia, que en x86 es la variante de repnz scasb asm a menos que pase -Werror o -fno-builtin. Los archivos relacionados con esto están en gcc/config/<platform>/<platform>.{c,md}

También es controlado por gcc / builtins.c. En caso de que se preguntara si y cómo se optimizó un strlen () a una constante, vea la función definida como tree c_strlen(tree src, int only_value) en este archivo. También controla cómo strlen (entre otros) se expande y se pliega (según la configuración / plataforma mencionada anteriormente)


Puedes usar este código, ¡cuanto más simple, mejor!

size_t Strlen ( const char * _str ) { size_t i = 0; while(_str[i++]); return i; }


definido en 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 == ''/0'') 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)



glibc 2.26 tiene varias implementaciones de ensamblaje optimizadas a mano de strlen

A partir de glibc-2.26 , un rápido:

git ls-files | grep strlen.S

en el árbol glibc muestra una docena de implementaciones de ensamblaje optimizadas a mano para todos los arcos principales y variaciones.

En particular, x86_64 solo tiene 3 variaciones:

sysdeps/x86_64/multiarch/strlen-avx2.S sysdeps/x86_64/multiarch/strlen-sse2.S sysdeps/x86_64/strlen.S

Una forma rápida y sucia de determinar cuál se usa, es depurar un programa de prueba:

#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] = ''/0''; result = strlen(s); assert(result == size - 1); return EXIT_SUCCESS; }

compilado con:

gcc -ggdb3 -std=c99 -O0 a.c

De buenas a primeras:

disass main

contiene:

callq 0x555555554590 <strlen@plt>

así que la versión libc está siendo llamada.

Después de unos pocos pasos de nivel de instrucción en eso, GDB alcanza:

__strlen_avx2 () at ../sysdeps/x86_64/multiarch/strlen-avx2.S:52 52 ../sysdeps/x86_64/multiarch/strlen-avx2.S: No such file or directory.

lo que me dice que se utilizó strlen-avx2.S .

Entonces, además confirmo con:

disass __strlen_avx2

y comparar el desmontaje con la fuente glibc.

No es sorprendente que se haya utilizado la versión AVX2, ya que tengo una CPU i7-7820HQ con fecha de lanzamiento Q1 2017 y AVX2, y AVX2 es la implementación de ensamblaje más avanzada, con fecha de lanzamiento Q2 2013, mientras que SSE2 es mucho más antigua de 2004.

Aquí es de donde viene una gran parte de la dureza de glibc: tiene una gran cantidad de código de ensamblaje escrito a mano con un arco optimizado.

Probado en Ubuntu 17.10, gcc 7.2.0, glibc 2.26.

-O3

TODO: con -O3 , gcc no usa strlen de glibc, solo genera un ensamblaje en línea, que se menciona en: https://.com/a/19885891/895245

¿Es porque puede optimizar aún mejor? Pero su salida no contiene instrucciones AVX2, por lo que siento que este no es el caso.

https://www.gnu.org/software/gcc/projects/optimize.html menciones:

Deficiencias del optimizador de GCC.

glibc tiene versiones de ensamblador en línea de varias funciones de cadena; GCC tiene algunas, pero no necesariamente las mismas en las mismas arquitecturas. Se podrían proporcionar entradas optab adicionales, como las de ffs y strlen, para varias funciones más, incluyendo memset, strchr, strcpy y strrchr.

Mis pruebas simples muestran que la versión -O3 es en realidad más rápida, por lo que GCC tomó la decisión correcta.

Consultado en: https://www.quora.com/unanswered/How-does-GCC-know-that-its-builtin-implementation-of-strlen-is-faster-than-glibcs-when-using-optimization-level-O3