Welche Funktion ist schneller: strcmp oder strlen?



  • SeppJ schrieb:

    Mich wundert auch, was an dem getline-Beispiel so schwer zu verstehen ist?

    Ersetze printf durch fwrite, dann funktioniert mein Code auch in deinem konstruierten Szenario. Und er ist echt schneller, weil in C++ standardmässig cin mit cout getied ist und mit stdio gesynct wird. Das kann man ausstellen, aber kein mir bekannter C++-Programmierer macht sowas.

    Es ist ein ungeschriebenes Gesetz, dass:
    Nutzereingabe <=> Eingabe UTF-8, ohne Kontrollzeichen und zeilenweise, entweder Länge überschaubar oder Geschwindigkeit irrelevant
    Binärstream <=> Länge wird angegeben

    Für die Interaktion mit dem Nutzer sollte auf GNU Readline zurückgegriffen werden (d.h. C++ nutzt hier auch C), für das Parsen von Config-Files kann auch mal 32'000 Zeichen als maximale Länge angenommen werden. Ansonsten ist etwas mit der Datei falsch (Zeilenenden der Datei vom Mac nicht erkannt); C++ würde da der Speicher ausgehen.

    Schnelles Parsen und C passt perfekt zusammen. An Bison/Flex kommt kein Stream ran.

    Es ist kein Zufall, dass diese theoretisch möglichen besten C-Lösungen in diesem Thread von niemandem vorgeführt werden.

    Mit anderen Worten lautet deine Argumentation: C++ ist besser als C weil die Standardbibliothek hat viel mehr fertige Algorithmen.
    Beweise für die Richtigkeit meiner Behauptung: getline ist für dich kein C.

    Nehmen wir einmal Hashtables. Alle scheinen sie zu brauchen, weder C noch C++98 haben welche vorzuweisen. Es gibt schöne C-Bibliotheken, die sie anbieten: https://developer.gnome.org/glib/stable/glib-Hash-Tables.html. Wenn ich mich recht erinnere, war die schneller als boost::unordered_map.
    Mit C++11 hat sich da nicht viel geändert, std::unordered_map ist weder sonderlich schneller noch langsamer, und wenn dann liegt der Unterschied im Algorithmus und nicht an der Sprache.



  • SeppJ schrieb:

    Diese Funktion gibt es aber in ANSI-C nicht.[/quote]
    invalid Argument :p

    natürlich ist das keine standard bibliotheksfunktion.
    aber, das eine nicht-standard funktion programmiert werden müsste, war doch von vornherein klar.


  • Mod

    kenner der schummler schrieb:

    aber, das eine nicht-standard funktion programmiert werden müsste, war doch von vornherein klar.

    Klar. Aber mach doch mal! Außerdem gibt das Programm immer noch was anderes aus als das C++-Programm.



  • objdump schrieb:

    Nehmen wir einmal Hashtables. Alle scheinen sie zu brauchen, weder C noch C++98 haben welche vorzuweisen. Es gibt schöne C-Bibliotheken, die sie anbieten: https://developer.gnome.org/glib/stable/glib-Hash-Tables.html. Wenn ich mich recht erinnere, war die schneller als boost::unordered_map.
    Mit C++11 hat sich da nicht viel geändert, std::unordered_map ist weder sonderlich schneller noch langsamer, und wenn dann liegt der Unterschied im Algorithmus und nicht an der Sprache.

    Es gibt hunderte Opensource C++ Hashtables in allen Variationen und für jeden Spezialfall - das hat C nicht für sich gepachtet. Aber alle C-Hashtables teilen sich die Eigenschaft dass sie nicht typsicher sind und von daher Fehlerquellen sind.

    Fakt ist ebenfalls dass es bei C++ wenigstens eine in der Stdlib gibt und bei C nicht.

    Schnelles Parsen und C passt perfekt zusammen. An Bison/Flex kommt kein Stream ran.

    Kennste Boost.Spirit? Ich behaupte es geht nicht schneller. Und Spirit läuft sogar auf Sprachebene und ist kein extra Tool.



  • Ethon schrieb:

    Kennste Boost.Spirit?

    Ja

    Ethon schrieb:

    Kennste Boost.Spirit? Ich behaupte es geht nicht schneller.

    Nein.

    Boost.Spirit ist ziemlich schnell, hat aber die Einschränkung nur die Sprachebene von C++ nutzen zu können. Es würde mich überraschen, wenn er aus den Strings "abc", "abd", "ac" den Code if (c[0]==a){if(c[1]==b){if(c[2]==d])else if (c[2]==d)}else if(c[1]==c)} generiert.
    Und selbst wenn, spätestens bei etwas grösseren Grammatiken ist dann Schluss aus Compilezeitgründen.

    Boost.Spirit wäre in C unmöglich; ich meine aber, dass hier der richtige Weg externe Code-Generatoren sind. Die Alternative ist eine echte Compilezeitsprache, aber dafür sind Templates ungeeignet. Und constexpr wird wohl nie mächtig genug.



  • Ich finde diese Diskussion ziemlich langweilig. Beide Seiten haben Recht und Unrecht zugleich.

    In C++ lässt sich ein Programm schnell und einfach entwickeln. Es ist bei Beachtung einfacher Regeln sehr sicher und flexibel. Trotz allem lässt es sich noch immer gut warten.

    In C lässt sich mit entsprechender Erfahrung ebenso recht schnell ein Programm schreiben. Trotzdem ist man hier auf sich alleine gestellt und muss genau wissen, was man tut. Da man noch mehr selber machen muss, spart man hier und da etwas Rechenzeit. Es lässt sich möglicherweise weniger gut warten, da man schnell etwas vergessen kann, was Fehler hervorruft.

    Da heutige C++ Compiler sehr gut optimieren, ist der Unterschied jedoch nicht mehr sonderlich groß. Es muss schon eine sehr performancekritische Software sein, dass man auf die Idee kommt alles in C zu schreiben. In dem konkretem Fall kann C wirklich im Vorteil sein. Aber dann muss auch der Programmierer ein absoluter Profi sein.

    Denn nur weil man in C programmiert, ist das Programm nicht automatisch schneller. Das zeigt ja schon die Eingangsfrage, wo erst strlen und dann strcmp aufgerufen werden sollte, was aus Performancesicht das absolute Gegenteil bewirkt.



  • In C lässt sich mit entsprechender Erfahrung ebenso recht schnell ein Programm schreiben.

    Ich glaube das Problem ist, dass im kommerziellen Einsatz Fremdbibliotheken verpönt, da sie ja potenziell fehlerbehaftet sein können. Also hat man im kommerziellen Einsatz eher keine Container-Datentypen außer Array's zur Verfügung.

    Also hat man mindestens 10 mal eine Liste für unterschiedliche Datentypen implementiert, wobei natürlich jede Implementierung auch Fehler enthalten kann. Und dann nimmt man eher ein Array als einen Heap, weil man ja möglichst fehlerfrei arbeiten wollen. Und dann spricht man von Effiziens, Schnelligkeit,... Du krieg ich regelmäßig Augen-Tripper.

    Nutzereingabe <=> Eingabe UTF-8, ohne Kontrollzeichen und zeilenweise, entweder Länge überschaubar oder Geschwindigkeit irrelevant

    Ja und dann kommt ein Kunde, hat aus Effiziensgründen alle Zeilen in eine gepackt und beschwert sich dass das Drecksprogramm abstürzt.

    Binärstream <=> Länge wird angegeben

    Merkwürdig. Bei der Datenübertragung mittels Socket's weiß ich davon nichts.

    Fixe Größenangaben taugen selten was.



  • Bitte ein Bit schrieb:

    Fixe Größenangaben taugen selten was.

    Nun, bei sockets muss ich aber angeben, wieviel ich maximal lesen kann. Diese Groesse ist fix.



  • Bitte ein Bit schrieb:

    Ja und dann kommt ein Kunde, hat aus Effiziensgründen alle Zeilen in eine gepackt und beschwert sich dass das Drecksprogramm abstürzt.

    Dann will ich aber nicht zeilenweise lesen, sondern nur bis zum nächsten Komma.

    Bitte ein Bit schrieb:

    Fixe Größenangaben taugen selten was.

    Fix heisst nicht zwingend fix zur Compilezeit. Ein Protokoll würde zuerst die Stringlänge und dann den Inhalt versenden.



  • objdump schrieb:

    Fix heisst nicht zwingend fix zur Compilezeit. Ein Protokoll würde zuerst die Stringlänge und dann den Inhalt versenden.

    Aehm, d.h. du liesst die "Stringlaenge" aus und erzeugst dann den Readbuffer? Halte ich fuer fragwuerdig, da einfach durch Fehler in der Laenge verherend sind. Klar kann man was drumherumbauen, aber ein fixer Eingabebuffer ist mir lieber.

    Btw. Was waere denn die Abstraktion fuer "String" + Laenge, wenn moeglich noch mit benutzerfreundlicher Funktionalitaet in C? In C++ ist es std::vector oder std::string!



  • knivil schrieb:

    Btw. Was waere denn die Abstraktion fuer "String" + Laenge, wenn moeglich noch mit benutzerfreundlicher Funktionalitaet in C? In C++ ist es std::vector oder std::string!

    std::string => GString
    Ansonsten kann sich das jeder selber schreiben, sind nicht 2000 Zeilen wie in C++, sondern nur 4

    struct benutzerfreundlicherString {
      size_t länge;
      char inhalt[];
    };
    struct benutzerfreundlicherString2 {
      size_t länge;
      char *inhalt;
    };
    

    Ist übrigens um einiges effizienter als C++

    void f(std::string s); // string wird kopiert, ein C++-Programmierer muss höllisch 
                            // aupassen das nicht unabsichtlich hinzuschreiben
    void f(std::string const& s); // indirektion size, doppelte indirektion inhalt
    
    void f(benutzerfreundlicherString *s); // einfache indirektion string+inhalt, nur 1 Pointer parameter
    void f(benutzerfreundlicherString2 s); // nur indirektion für inhalt
    

  • Mod

    objdump schrieb:

    Ansonsten kann sich das jeder selber schreiben, sind nicht 2000 Zeilen wie in C++, sondern nur 4

    Kein Wunder. Der kann schließlich nichts. Wie erzeugt man einen? Wie kopiert man? Wie bekommt man Inhalt hinein? Wie hängt man sie aneinander? Wie durchsucht man die Zeichenkette? Hat der Code auch kein Speicherloch? Was glaubst du, wieso der GString eine so lange Dokumentation (und Implementierung) hat?

    Das sind ganz grundlegende Fragen für die Zeichenkettenverarbeitung. Du darfst zur Implementierung auch gerne string.h benutzen.

    Wenn ich fies wäre, würde ich noch nach Optimierungen (SSO oder COW) fragen, aber ich sehe ja, dass du schon mit den einfachen Dingen überfordert bist.



  • Nun, bei sockets muss ich aber angeben, wieviel ich maximal lesen kann. Diese Groesse ist fix.

    Also die einzigste Einschränkung war bei recv(). Ein Test mit der Puffergröße von 2 Byte funktionierte auch problemlos, er las der Daten immer in 1 Byte Blöcken ein und fügte sie dem std::string hinzu.

    Dann will ich aber nicht zeilenweise lesen, sondern nur bis zum nächsten Komma.

    Sofern die Textdatei ein Komma enthält... Dummes Beispiel, ich weis.

    Dein String-Vorschlag hakt an vielen Ecken und Kanten, wie es SeppJ schon sagt. Spiele doch einfach mal dummer Programmierer und schaue wie sich dein C String verhält.

    benutzerfreundlicherString2 s1, s2;
    
    s1 = malloc(10);
    strcpy(s2.inhalt, "Hallo");
    
    s2 = s1;
    free(s1.inhalt);
    strcpy(s2.inhalt, "Welt");
    

    Ist übrigens um einiges effizienter als C++

    Und schon wieder der Begriff Effizienter? Entschludigung aber so langsam wird in meinen Augen der Begriff Effizienzs ein Indiz für Inkompentenz.

    Dumme Frage. Überprüfst du auch ordentlich ob dein Zeiger auch nicht in Nirwana zeigt. Also überprüfst du auf NULL, 0xCCCCCCCC, ... ?

    Auch ein C Programierer muss höllich auf Übergabeparameter achten! Programmiersprache schützt vor Dummheit nicht.

    typedef struct 
    {
      char Name[512];
      char Nachname[512];
    } tPerson;
    
    void f(tPerson s);
    


  • Bitte ein Bit schrieb:

    typedef struct 
    {
      char Name[512];
      char Nachname[512];
    } tPerson;
    
    void f(tPerson s);
    

    Ja, es gibt natürlich schlechten C-Code.

    Mindestens

    typedef struct tPerson
    {
      char Name[512];
      char Nachname[512];
    } tPerson;
    
    void f(tPerson s);
    

    besser

    typedef struct tPerson
    {
      const char *Name;
      const char *Nachname;
    } tPerson;
    

    wenn Lokalität wichtig ist (und diese Art Optimierung habe ich in C++ noch nie gesehen)

    typedef struct tPerson {
      size_t name_size, nachname_size;
      char data[];
    } tPerson;
    tPerson *person_neu(benutzerfreundlicherString vorname, benutzerfreundlicherString nachname)
    { tPerson *p = malloc(sizeof(tPerson) + vorname.size + nachname.size);
      p->name_size = vorname.size;
      p->nachname_size = nachname.size;
      strcpy(vorname.inhalt, p.data);
      strcpy(nachname.inhalt, p.data + vorname.size);
      return p;
    }
    void person_gib_frei(tPerson *p) { free(p); }
    const char *person_vorname(tPerson *p) { return p->data; }
    const char *person_nachname(tPerson *p) { return p->data + p->vorname_size; }
    


  • wenn Lokalität wichtig ist (und diese Art Optimierung habe ich in C++ noch nie gesehen)

    Ich habe sowas schon of in C++ geschrieben, wobei deine Loesung umstaendlich ist. Du kannst auch einfach zwei char* haben und ein Array mit den beiden Namen getrennt durch ein '\0', dann koennen sie als normale C-Strings behandelt werden, gleich out-of-the-box. Darueber hinaus gibt es mehr Moeglichkeiten ueber placement new oder Allokatoren.

    C++-Programmierer muss höllisch
    

    Ein C Programmierer auch, ein Chirurge auch, ein Physiker auch, ... Das Problem: Wieviel kann der Kompiler mir davon abnehmen? Das ist bei idiomatischem C++ (man kann in C++ auch C programmieren) wesentlich mehr als in C.

    @objdump: Diese Antwort ist nicht fuer dich, aber fuer alle Unvoreingenommenen.



  • C++ lassen sich sehr schöne Dinge mit eigenen Allokatoren lösen. Da bekommt man zb. SSO gratis und solche Lokalitäts-Optimierungen sind auch gut machbar.



  • Kleine Frage am Rand:
    Was versteht man unter Lokalität?

    typedef struct tPerson {
      size_t name_size, nachname_size;
      char data[];  
    } tPerson;
    tPerson *person_neu(benutzerfreundlicherString vorname, benutzerfreundlicherString nachname)
    { tPerson *p = malloc(sizeof(tPerson) + vorname.size + nachname.size);
      // Warum prüfst du hier nicht darauf dass p != NULL ist ?
      p->name_size = vorname.size;
      p->nachname_size = nachname.size;
      // Wer garantiert mir dass vorname und nachname initialisiert sind ?
      strcpy(vorname.inhalt, p.data); // Du kopierst hier p.data nach vorname.inhalt!
      strcpy(nachname.inhalt, p.data + vorname.size); // Du kopierst hier p.data nach vorname.inhalt!
      return p;
    }
    
    // Die Funktion person_neu() erfordert Disziplin. Denn wer ruft person_gib_frei() auf? Und wie?
    void person_gib_frei(tPerson *p) { free(p); }}
    

    Siehst du, auch du hast nach Strich und Faden dein Code verbuggt.

    Ich bin mal gemein. Ich spiele mal Dau!

    benutzerfreundlicherString v;
    benutzerfreundlicherString n;
    tPerson* P;
    tPerson* P2;
    
    v.inhalt = malloc(256);
    strcpy(v.inhalt, "Tom")
    P = person_neu(v, n);
    
    *P2 = P;
    person_gib_frei(&P);
    printf(person_vorname(&P2));
    

    Du arbeitest viel mit Zeigern.



  • objdump schrieb:

    void f(std::string s); // string wird kopiert, ein C++-Programmierer muss höllisch 
                            // aupassen das nicht unabsichtlich hinzuschreiben
    void f(std::string const& s); // indirektion size, doppelte indirektion inhalt
    
    void f(benutzerfreundlicherString *s); // einfache indirektion string+inhalt, nur 1 Pointer parameter
    void f(benutzerfreundlicherString2 s); // nur indirektion für inhalt
    

    Danke für die guten Beispiele. Ein Beispiel von Dir wurde ja schon kommentiert und auf Fehler untersucht. Damit konnten wir beweisen, dass man bei C höllisch aufpassen muss.

    Bei C++ muss man höllisch aufpassen, dass bei der Parameterübergabe der std::string nicht kopiert wird? Wenn man da nicht aufpasst und by value übergibt, dann läuft das Programm nicht mit der optimalen Performance.

    Bei C muss man wohl nur ein bisschen aufpassen. Pufferüberläufe können mal passieren, aber wenn man ein bisschen aufpasst, dann geht das schon. Wenn man da nicht aufpasst, dann stürzt das Programm ab.

    Ich persönlich finde ein Programm, welches nicht mit optimaler Performance arbeitet besser, als ein Programm, welches hin und wieder mal abstürzt.

    Auch muss man bei C ein wenig aufpassen, dass man beispielsweise das "person_gib_frei" geeignet aufruft. Und wenn man die Person kopiert hat, dass man die richtige Kopie und nur die eine frei gibt. Wenn man das nicht tut, dann läuft der Speicher voll. Auch kein gutes Verhalten.

    Da ist mir ein Programm, welches nicht mit optimaler Performance arbeitet doch lieber.



  • @Ethon: Zeig mir mal wie ein eigener Allokator aussieht.
    @Knivil: Du kriegst aber die Stringlänge nicht in konstanter Zeit raus.

    Bitte ein Bit schrieb:

    Ich spiele mal Dau!

    Falscher Ansatz, wenn ich Dau spiele, ist C++ auch nicht sicher.

    C ist keine Sprache für Daus, die sollen auf C++ wechseln. Dass NULL hier als Parameter sinnlos ist, dürfte selbsterklärend sein (spätestens nach dem nächsten Segfault, der unmittelbar auftritt) und dass ich die Parameter für strcpy vertauscht habe, meldet mir mein Compiler. Zeigerfehler, die auch mir passieren können, findet valgrind im nächsten Test.

    tntnet schrieb:

    Ich persönlich finde ein Programm, welches nicht mit optimaler Performance arbeitet besser, als ein Programm, welches hin und wieder mal abstürzt.

    Abstürze und UB sind kein so schlimmes Problem, dafür gibt es Tests.

    Einigen wir uns darauf: C++ arbeitet schnell mal nicht mit optimaler Performance, erleichtert aber unter gewissen Umständen die Entwicklung. C ist schwieriger, man hat aber guten einen Überblick und eine gute Kontrolle darüber, was passiert.


  • Mod

    Bezüglich der Performance der Stringübergabe haben übrigens std::move und Link Time Optimierung angerufen und wollten sich mal vorstellen, dass sie seit einiger Zeit hier in der Gegend wohnen. Die LTO behauptet übrigens, sich auch mit C auszukennen.
    Ich habe hier auch noch eine Postkarte von einer gewissen Copy on write Optimierung, auf der ganz viel zu dem Thema steht. Die Karte scheint aber schon etwas älter zu sein, ich weiß nicht, ob die noch aktuell ist.
    🤡


Anmelden zum Antworten