Wo ist die Implementierung von strlen() in GCC?

Lesezeit: 8 Minuten

Benutzer-Avatar
Chris Tonkinson

Kann mir jemand die Definition von zeigen strlen() im GCC? Ich suche jetzt seit ungefähr einer halben Stunde nach Version 4.4.2 (während ich wie verrückt google) und kann anscheinend nicht finden, wo strlen() tatsächlich umgesetzt wird.

Benutzer-Avatar
Mark Ruschakoff

Sie sollten in glibc suchen, nicht in GCC – es scheint in definiert zu sein strlen.c – Hier ist ein Link zu strlen.c für Glibc-Version 2.7… Und hier ist ein Link zu dem glibc SVN-Repository online für strlen.c.

Der Grund, den Sie sich ansehen sollten glibc und nicht gcc ist:

Als Bibliothek wird die GNU-C-Bibliothek verwendet das C-Bibliothek im GNU-System und den meisten Systemen mit dem Linux-Kernel.

  • Meh, das ist nicht sehr optimiert. Zumindest mit Visual C++ bekommen wir eine anständige Assemblersprache strlen.

    – ganz

    14. November 2009 um 4:55 Uhr

  • “Die GNU-C-Bibliothek ist in erster Linie als portable und leistungsstarke C-Bibliothek konzipiert.” Ich vermute, sie legen vielleicht mehr Gewicht auf den Portabilitätsteil.

    – Mark Rushakoff

    14. November 2009 um 5:00 Uhr

  • Ähm, das ist die portable Version, überprüfen Sie das Sysdeps-Verzeichnis auf die Versionen, die tatsächlich in Ihre Programme gehen. Das heißt, wenn GCC nicht zuerst dort ankommt und den Aufruf durch eine Inline-Version ersetzt, aber dann hätte OP ihn vermutlich schon einmal gesehen. cvs.savannah.gnu.org/viewvc/libc/sysdeps/x86_64/…

    Benutzer14554

    14. November 2009 um 5:39 Uhr

  • Diese C-Version ist tatsächlich extrem optimiert (obwohl das manuelle Aufrollen der Schleife ziemlich idiotisch ist). Sie werden es selbst mit asm schwer haben, es zu schlagen.

    – R.. GitHub HÖR AUF, EIS ZU HELFEN

    24. Februar 2011 um 4:37 Uhr

  • @toto Dies ist ab glibc 2.26 nicht mehr der Fall, es gibt jetzt handoptimierte Assembly-Implementierungen für alle wichtigen Archs: stackoverflow.com/a/50199212/895245

    – Ciro Santilli OurBigBook.com

    6. Mai 2018 um 11:37 Uhr

Mir ist klar, dass diese Frage 4 Jahre alt ist, aber gcc wird sie oft enthalten besitzen Kopie von strlen, wenn Sie dies nicht tun #include <string.h> und keine der Antworten (einschließlich der akzeptierten Antwort) berücksichtigt dies. Wenn Sie es vergessen, erhalten Sie eine Warnung:

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

und gcc fügt seine Kopie ein, die auf x86 die Variante repnz scasb asm ist, es sei denn, Sie übergeben -Werror oder -fno-builtin. Die dazugehörigen Dateien sind in gcc/config/<platform>/<platform>.{c,md}

Es wird auch von gcc/builtins.c gesteuert. Falls Sie sich gefragt haben, ob und wie ein strlen() auf eine Konstante optimiert wurde, sehen Sie sich die Funktion definiert als an tree c_strlen(tree src, int only_value) in dieser Datei. Es steuert auch, wie strlen (unter anderem) erweitert und gefaltet wird (basierend auf der zuvor erwähnten Konfiguration/Plattform).

  • Die eingebaute GCC-Version wird auch unter verwendet -O3 in GCC 7.2 auch wenn #include <string.h>: stackoverflow.com/a/50199212/895245

    – Ciro Santilli OurBigBook.com

    6. Mai 2018 um 11:50 Uhr

Benutzer-Avatar
hyperlogisch

Hier ist die bsd Implementierung

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

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

  • Warte immer noch auf den Tag, an dem ein Compiler daraus brauchbar schnellen Maschinencode generieren kann…. Derzeit ist es weniger als halb so schnell wie ein optimierter C Ausführung.

    – R.. GitHub HÖR AUF, EIS ZU HELFEN

    24. Februar 2011 um 4:38 Uhr

  • @R .. ICC kann Schleifen wie diese normalerweise automatisch vektorisieren. gcc/clang kann das nicht: Sie vektorisieren nur Schleifen automatisch, bei denen die Trip-Zählung vor der ersten Iteration bekannt ist. (dh sie sind bei Suchschleifen nutzlos.)

    – Peter Cordes

    2. August 2019 um 12:46 Uhr

  • Was ist die Idee hinter diesem falsch platzierten Semikolon? Ich frage mich wirklich, warum all diese alten C-Codes so geschrieben sind: D

    – Sekomer

    9. Juni um 23:38 Uhr

Benutzer-Avatar
Jenseits

definiert 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)

Benutzer-Avatar
Ciro Santilli OurBigBook.com

glibc 2.26 hat mehrere handoptimierte Assembler-Implementierungen von strlen

Ab glibc-2.26eine schnelle:

git ls-files | grep strlen.S

im Glibc-Baum zeigt ein Dutzend von Hand optimierte Implementierungen für alle wichtigen Bögen und Variationen.

Insbesondere x86_64 allein hat 3 Variationen:

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

Eine schnelle und schmutzige Methode, um festzustellen, welches verwendet wird, besteht darin, ein Testprogramm schrittweise zu debuggen:

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

zusammengestellt mit:

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

Abseits der Fledermaus:

disass main

enthält:

callq  0x555555554590 <strlen@plt>

also wird die libc-version aufgerufen.

Nach einigen si Anweisungsebene tritt in das ein, GDB erreicht:

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

was mir das sagt strlen-avx2.S wurde benutzt.

Dann bestätige ich weiter mit:

disass __strlen_avx2

und vergleichen Sie die Disassemblierung mit der Glibc-Quelle.

Es ist nicht verwunderlich, dass die AVX2-Version verwendet wurde, da ich eine habe i7-7820HQ CPU mit Startdatum Q1 2017 und AVX2-Unterstützung und AVX2 ist die am weitesten fortgeschrittene der Assembly-Implementierungen, mit dem Startdatum Q2 2013, während SSE2 ist viel älter von 2004.

Hier kommt ein großer Teil der Hardcoreness von glibc her: es hat viel Arch-optimierten, handgeschriebenen Assembler-Code.

Getestet in Ubuntu 17.10, gcc 7.2.0, glibc 2.26.

-O3

Zu tun mit -O3gcc verwendet nicht die von glibc strlengeneriert es nur eine Inline-Assembly, die unter https://stackoverflow.com/a/19885891/895245 erwähnt wird

Liegt es daran, dass es noch besser optimieren kann? Aber seine Ausgabe enthält keine AVX2-Anweisungen, daher denke ich, dass dies nicht der Fall ist.

https://www.gnu.org/software/gcc/projects/optimize.html erwähnt:

Mängel des Optimierers von GCC

glibc hat Inline-Assembler-Versionen verschiedener String-Funktionen; GCC hat einige, aber nicht unbedingt dieselben auf denselben Architekturen. Zusätzliche optab-Einträge, wie die für ffs und strlen, könnten für mehrere weitere Funktionen bereitgestellt werden, darunter memset, strchr, strcpy und strrchr.

Meine einfachen Tests zeigen, dass die -O3 Version ist tatsächlich schneller, also hat GCC die richtige Wahl getroffen.

Gefragt bei: https://www.quora.com/unanswered/How-does-gcc-now-that-its-builtin-implementation-of-strlen-is-faster-than-glibcs-when-using-optimization-level-O3

Obwohl der ursprüngliche Poster dies möglicherweise nicht wusste oder danach gesucht hat, fügt gcc intern eine Reihe sogenannter “eingebauter” c-Funktionen ein, die es selbst definiert, einschließlich einiger der mem*()-Funktionen und (abhängig von der gcc-Version) strlen. In solchen Fällen wird die Bibliotheksversion im Wesentlichen nie verwendet, und es ist streng genommen nicht korrekt, die Person auf die Version in glibc zu verweisen. (Es tut dies aus Leistungsgründen – zusätzlich zu der Verbesserung, die das Inlining selbst erzeugt, „weiß“ gcc bestimmte Dinge über die Funktionen, wenn es sie bereitstellt, wie zum Beispiel, dass strlen eine reine Funktion ist und dass es das kann mehrfache Aufrufe wegoptimieren, oder bei den mem*()-Funktionen, dass kein Aliasing stattfindet.)

Weitere Informationen hierzu finden Sie unter http://gcc.gnu.org/onlinedocs/gcc/Other-Builtins.html

Benutzer-Avatar
faran

Suchst du danach? strlen() Quelle. Siehe die Git-Repository für mehr Informationen. Das glibc-Ressourcenseite enthält Links zu den Git-Repositories, wenn Sie sie abrufen möchten, anstatt sich die Webansicht anzusehen.

1366110cookie-checkWo ist die Implementierung von strlen() in GCC?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy