Domanda

Qualcuno mi può puntare alla definizione di strlen() in GCC? Ho grep 4.4.2 di rilascio per circa mezz'ora ora (mentre Googling come un matto) e io non riesco a trovare dove strlen() è effettivamente implementata.

È stato utile?

Soluzione

Si dovrebbe essere alla ricerca di glibc, non GCC - sembra essere definita in strlen.c - ecco un link al strlen.c per glibc versione 2.7 ... Ed ecco un link al glibc repository SVN on-line per strlen.c .

Il motivo si dovrebbe essere guardando glibc e non gcc è:

  

La libreria GNU C è utilizzato come il C biblioteca nel sistema GNU e la maggior parte dei sistemi con il kernel Linux.

Altri suggerimenti

Mi rendo conto che questa domanda è 4yrs vecchio, ma gcc spesso includono la sua proprio copia di strlen se non #include <string.h> e nessuna delle risposte (compreso il accettato risposta) conto per questo. Se si dimentica, si otterrà un messaggio di avviso:

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

e gcc sarà inline la sua copia, che su x86 è il repnz variante scasb asm a meno che non si passa -Werror o -fno-incorporato. I file relativi a questo sono in gcc/config/<platform>/<platform>.{c,md}

E 'controllato anche da gcc / builtins.c. Nel caso in cui si chiedeva se e come uno strlen () è stato ottimizzato per una costante, vedi la funzione definita come tree c_strlen(tree src, int only_value) in questo file. Controlla anche strlen (tra gli altri) si espande e piegato (basato sui config / piattaforma precedentemente menzionato)

Ecco il bsd realizzazione

size_t
strlen(const char *str)
{
        const char *s;

        for (s = str; *s; ++s)
                ;
        return (s - str);
}

definito in 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)

E 'questo quello che stai cercando? strlen () fonte. Vedere la git repository per ulteriori informazioni. Il glibc risorse pagina ha collegamenti ai repository git, se si vuole prendere loro piuttosto che guardando il panorama web.

Anche se il poster originale non può aver conosciuto questo o stato alla ricerca di questo, gcc inlines internamente una serie di cosiddette funzioni c "built-in", che definisce da solo, tra cui alcuni dei mem * () funzioni e ( a seconda della versione di gcc) strlen. In questi casi, la versione della libreria è essenzialmente mai usato, e indicando la persona alla versione glibc non è a rigor di termini corretti. (Lo fa per ragioni di prestazioni - oltre al miglioramento che inlining stessa produce, gcc "sa" alcuni dati riguardanti le funzioni allorché fornisce loro, come, per esempio, che è una funzione strlen puro e che può quindi ottimizzare la via più chiamate, o, nel caso della mem * () funzioni che non aliasing è in atto.)

Per ulteriori informazioni su questo, vedere http://gcc.gnu.org /onlinedocs/gcc/Other-Builtins.html

Google Code Search è un buon punto di partenza per le domande del genere. Di solito indicano varie fonti e implementazioni diverse di una funzione.

Nel tuo caso particolare: GoogleCodeSearch (strlen)

Google Code Search è stato completamente spento il marzo 2013

glibc 2.26 è ottimizzato vari mano implementazioni assemblaggio di strlen

A partire dal glibc-2.26, un rapido:

git ls-files | grep strlen.S

nell'albero glibc mostra una dozzina di montaggio implementazioni ottimizzato a mano per tutti i principali archi e varianti.

In particolare, x86_64 solo ha 3 varianti:

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

Un modo rapido e sporco per determinare quella che viene utilizzato, è al passo il debug di un programma di test:

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

compilato con:

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

fuori del blocco:

disass main

contiene:

callq  0x555555554590 <strlen@plt>

così la versione libc viene chiamato.

Dopo pochi passi a livello di istruzioni si in quella, GDB raggiunge:

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

che mi che dice strlen-avx2.S è stato utilizzato

.

Poi, ho ulteriormente confermo con:

disass __strlen_avx2

e confrontare lo smontaggio con la sorgente glibc.

Non è sorprendente che la versione AVX2 è stato utilizzato, dal momento che ho un i7-7820HQ CPU con data di lancio Q1 2017 e il supporto AVX2, e AVX2 è il più avanzato delle implementazioni di montaggio, con la data di lancio Q2 2013, mentre SSE2 è molto più antica rispetto al 2004.

E 'qui che una grande parte della hardcoreness di glibc proviene da:. Ha un sacco di arco ottimizzato codice assembly scritto a mano

Testato in Ubuntu 17.10, gcc 7.2.0, glibc 2.26.

-O3

TODO: con -O3, gcc non usa strlen di glibc, si genera solo assembly inline, che è menzionato in: https: // stackoverflow.com/a/19885891/895245

E 'perché è in grado di ottimizzare ancora migliore? Ma la sua uscita non contiene istruzioni AVX2, quindi mi sento che questo non è il caso.

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

  

Le carenze di ottimizzatore del GCC

     

glibc ha versioni assembler in linea di varie funzioni di stringa; GCC ha alcuni, ma non necessariamente le stesse sulle stesse architetture. voci optab aggiuntivi, come quelli per FFS e strlen, potrebbero essere forniti per diversi funzioni tra cui memset, strchr, strcpy e strrchr.

I miei semplici test mostrano che la versione -O3 è effettivamente più veloce, in modo da GCC fatto la scelta giusta.

Alla domanda all'indirizzo: https://www.quora.com/unanswered/How-does-GCC-know-that-its-builtin-implementation-of-strlen- è-veloce-che-glibcs-quando-con-ottimizzazione a livello-O3

Mi rendo conto che questa è la vecchia questione, è possibile trovare i sorgenti del kernel di linux a github qui , e l'attuazione 32 bit per strlen () è stato trovato nel strlen_32 .c su GitHub. Il file citato ha questa implementazione.

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

È possibile utilizzare questo codice, il più semplice, meglio è!

size_t Strlen ( const char * _str )
{
    size_t i = 0;
    while(_str[i++]);
    return i;
}
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top