Welche Funktion ist schneller: strcmp oder strlen?



  • It0101 schrieb:

    SeppJ schrieb:

    fairy story schrieb:

    The point ...
    dass es Leute gibt, die einem weismachen wollen, mit C++ käme ein performanteres Endprodukt als mit C heraus.

    Ich warte immer noch auf ein performantes qsort oder getline. Natürlich geht das. Aber kein Mensch macht das, da es viel zu viel Aufwand ist. Selbst wenn man in einem Internetforum recht behalten möchte ist das anscheinend zu viel Aufwand. Welchen motivierenderen Anlass gäbe es denn, wenn nicht Klugscheißerei? 🙂

    Der Punkt ist, dass es gar nicht darum geht, an jeder Stelle die wesentlich bequemere C++-Variante durch eine aufwendig zu entwickelnde C-Lösung zu ersetzen. Da sind wir uns sicherlich einig, dass std::getline sicherlich kein gutes Beispiel für Perfomanceverbesserung ist. Warum? Weil solche Stellen selten Performancerelevant sind.

    Wenn es aber an irgendeiner extrem kritischen Stelle an der Performance krankt, dann macht es auf jeden Fall Sinn, über eine C-Variante nachzudenken.

    Der Punkt ist, daß C++ nicht nur wesentlich bequemer ist, sondern tendenziell schneller. C++-Programme, wenn sie nicht mini-klein sind, werden inzwischen ganz von alleine schneller als vergleichbare C-Programme.


  • Mod

    It0101 schrieb:

    Wenn es aber an irgendeiner extrem kritischen Stelle an der Performance krankt, dann macht es auf jeden Fall Sinn, über eine C-Variante nachzudenken.

    Nachdenken allgemein: Klar! Aber wieso über C? Ist das inhärent schneller? Eher im Gegenteil, man müsste sehr viel Aufwand betreiben, um gleich schnell zu sein. Mehr Arbeit für das gleiche Ergebnis.

    Mich wundert auch, was an dem getline-Beispiel so schwer zu verstehen ist? Mir geht es natürlich nicht um Performancevergleiche bei der Eingabeverarbeitung. Es geht darum, dass C extrem aufwändig wird, sobald es irgendwie dynamisch wird. Eine Lösung die das gleiche macht wie die drei Zeilen C++ und dabei noch gleich schnell ist, hätte gewiss deutlich über 100 Zeilen und stundenlange Entwicklungsarbeit. Das ist die Stärke von C++. Gleiche Performance wie die bestmögliche C-Lösung bei einem Bruchteil des Entwicklungsaufwandes.

    Es ist kein Zufall, dass diese theoretisch möglichen besten C-Lösungen in diesem Thread von niemandem vorgeführt werden. Der große C-Verteidiger objdump muss auf billige Tricks zurückgreifen (Miniprogramme unter Nutzung von Anomalien im C++-Standard wie vector<bool>; Optimierung des Problems auf unportable und gefährliche Features wie VLAs). Der Grund ist, dass niemand diese Lösungen programmieren will, da es viel zu viel Arbeit ist.



  • SeppJ schrieb:

    Der Grund ist, dass niemand diese Lösungen programmieren will, da es viel zu viel Arbeit ist.

    Wir haben einen Server, der in Spitzenzeiten pro Sekunde bis zu 100.000 Messages verarbeiten muss, ohne größere Latenzen reinzuzaubern. Das System ist überlebenswichtig und da kannst du sicher sein, dass uns kein Aufwand zu hoch ist, um da noch das eine oder andere Prozent Performance rauszuholen. Auch aufwändige C-Lösungen sind da relevant.



  • SeppJ schrieb:

    Falls es doch irgendwelche verrückte Lernwilligen gibt, die eine differenzierte Meinung zu schätzen wissen, dann mögen sie eine Fallunterscheidung machen:

    • < 1 MB: VLA (oder in C++ statisches Array)
    • < 100 MB: malloc/vector<char>
    • größer: vector<bool>

    (Größenangaben geschätzt)
    [/list]

    Ich nehme eher

    • < 4k: array (VLA würde auch gehen)
    • sonst: malloc/vector<char>

    Wenns klein und schnell sein soll, manchmal auch einen selbergebauten vector, der drunter ein array hat, also nicht über die zur Compilezeit fest vorgegebene Größe wachsen kann.



  • SeppJ schrieb:

    Das ist gar kein C, auch kein C11.

    was soll das sonst sein? das ist valide c-syntax.
    es wird eine funktion aufgerufen, also das, was auch dein code macht.


  • Mod

    kenner der schummler schrieb:

    es wird eine funktion aufgerufen, also das, was auch dein code macht.

    Diese Funktion gibt es aber in ANSI-C nicht.



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

Anmelden zum Antworten