mikrocontroller.net

Forum: PC-Programmierung Laufzeiten und GNU/Watcom C


Announcement: there is an English version of this forum on EmbDev.net. Posts you create there will be displayed on Mikrocontroller.net and EmbDev.net.
Autor: Joachim D. (Firma: JDCC) (scheppertreiber)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Servus,

ich habe ein gößeres Belegarchiv, abfragbar übers Internet.

Alles mit dem Watcom-Compiler in C geschrieben, läuft stabil
und flott.

Ein Kunde möchte sich per SSL einloggen und ich versuche gerade
da OpenSSL mit einzustricken, Da das alles mit GNU erzeugt ist,
verwende ich den da halt auchmal.

Problem: Ich lese, um den Benutzer zu authentifizieren, eine Rechtedatei 
mit 72 Benutzergruppemn und knapp 15000 Benutzern (3,5 MB).

Mit Watcom kompiliert: Laufzeit 100 msec
Mit GNU kompiliert: Laufzeit 17 sec (!)
Code ist identisch.

Ich verstehe das irgendwie nicht. Wonach müßte ich da suchen?

Autor: Jörg W. (dl8dtl) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Joachim D. schrieb:
> Wonach müßte ich da suchen?

Das ist nur so schwer zu sagen. Kannst du denn ein paar Codeschnipsel, 
die das fragliche Datei-Handling vornehmen, posten?

Wenn ich sowas simples aufsetze:
#include <stdio.h>

int
main(int argc, char **argv)
{
  if (argc < 2)
    return 1;

  FILE *f;
  if ((f = fopen(argv[1], "r")) == NULL)
    return 2;

  char line[200];
  int lineno = 0;

  while (fgets(line, sizeof line, f)) {
    lineno++;
  }
  printf("Read %d lines\n", lineno);

  return 0;
}

und auf eins der Dictionaries loslasse, geht das in Millisekunden:
% time ./foo /usr/share/dict/swiss 
Read 356108 lines
0.010u 0.000s 0:00.01 100.0%    0+0k 0+0io 0pf+0w
% time ./foo /usr/share/dict/ngerman 
Read 356008 lines
0.011u 0.000s 0:00.01 100.0%    0+0k 6672+0io 0pf+0w

Beide Dateien sind 4,5 MiB groß, also in dieser Hinsicht ungefähr 
vergleichbar mit deiner.

Autor: Joachim D. (Firma: JDCC) (scheppertreiber)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hmmmm...

der Code ist ja gleich. Ich habe so das "Gefühl" da passiert
irgndwoanders etwas. Ich habe nur kaum Erfahrungen mit dem
GNU - und eigentlich keinen Plan wo da die Fallstricke sein
könnten. So einenn extremen Laufzeitunterschied habe ich
noch nie erlebt. Das ist fast wie GWBASIC und C :)

Autor: Yalu X. (yalu) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Selbst ein zu Jörgs Testprogramm äquivalentes Python-Programm braucht
nur 66 ms. Der Großteil der Zeit entfällt dabei auf das Laden und
Starten des Interpreters. Der eigentliche Einlesevorgang dauert gerade
einmal 3 ms.

Joachim D. schrieb:
> Mit Watcom kompiliert: Laufzeit 100 msec
> Mit GNU kompiliert: Laufzeit 17 sec (!)

Der Faktor von 170 lässt sich fast nur durch einen Programmfehler
(undefined Behavior, Buffer Overflow o.ä.) erklären, der dazu führt,
dass das Programm – abhängig vom verwendeten Compiler – Dinge tut, die
es gar nicht tun sollte.

Mach den Fehler weg, dann wird das Programm auch mit GCC in 100 ms (oder
noch schneller) laufen ;-)

Autor: rµ (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
GNU gprof sollte weiterhelfen, damit wird man recht schnell eingrenzen 
können was die 17s an Zeit kostet. Vermutlich irgendein unbeabsichtigter 
Systemaufruf pro Zeichen.

Autor: Joachim D. (Firma: JDCC) (scheppertreiber)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Den Fehler (ich denke nicht daß da einer ist) würde ich
ja gerne wegmachen. Das Problem muß vor dem Laden auftreten
(ok, 100 ms sind lange, aber ich lese das dann noch in interne
Strukturen ein).

Profiler wäre mal einen Versuch wert ...

Autor: rµ (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Es ist möglich, dass die C-Lib vom Watcom beim Einlesen etwas anders 
puffert als die C-Library vom GNU. Das kann so einen Unterschied schon 
ausmachen (Hunderte Systemaufrufe vs. Millionen).

Raten und herumstochern kann man immer noch wenns Profilen nichts ergibt 
;-)

Autor: derMosphet (Gast)
Datum:

Bewertung
1 lesenswert
nicht lesenswert
Compiler Optimierungen (-O2) sind eingeschaltet?

Autor: Joachim D. (Firma: JDCC) (scheppertreiber)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
derMosphet schrieb:
> Compiler Optimierungen (-O2) sind eingeschaltet?

gibt 15,8 sec

Autor: mh (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Joachim D. schrieb:
> derMosphet schrieb:
>> Compiler Optimierungen (-O2) sind eingeschaltet?
>
> gibt 15,8 sec

Also hast du vermutlich nen Bug in deinem Program. Du kannst noch clang 
testen. Der Aufwand sollte minimal sein, da die Compileroptionen in der 
Regel identisch sind.

Autor: Joachim D. (Firma: JDCC) (scheppertreiber)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Klar ist da ein bug ;)

time( NULL) gibt auf einmal 0 ??? Gerade gefunden.

Autor: mh (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Joachim D. schrieb:
> Klar ist da ein bug ;)
>
> time( NULL) gibt auf einmal 0 ??? Gerade gefunden.

Ein weiteres Symptom. Vielleicht solltest du auch mal einen Blick auf 
die Sanitizer werfen, die gcc bietet (-fsanitize=...).

Autor: Joachim D. (Firma: JDCC) (scheppertreiber)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Beim watcom macht er mit der Option 'zp1' daqs structure aligment
auf 1 Byte. Ich brauche das programmintern. Das könnte damit etwas
zu tun haben ...

Mal sehen wie der GCC das so macht ;)

Autor: mh (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Joachim D. schrieb:
> Beim watcom macht er mit der Option 'zp1' daqs structure aligment
> auf 1 Byte. Ich brauche das programmintern. Das könnte damit etwas
> zu tun haben ...
>
> Mal sehen wie der GCC das so macht ;)

Du änderst mit einer Compileroption das Alignement für alle Strukturen?

Autor: Joachim D. (Firma: JDCC) (scheppertreiber)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
mh schrieb:
> Du änderst mit einer Compileroption das Alignement für alle Strukturen?

Ja.

Autor: Joachim D. (Firma: JDCC) (scheppertreiber)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Das ganze SSL-Zeuch mal totgelegt und #pragma pack(1) ganz
oben ins erste Headerfile.

Es ändert sich nichts.

Autor: rµ (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Auf welcher Plattform läuft das eigentlich? Windows oder Linux?

Bei C kanns scheidet eine andere Container-Implementierung oder 
plötzlich pathologische Bibliotheks-Hash-Funktion aus.

Autor: Joachim D. (Firma: JDCC) (scheppertreiber)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Windows 10.

Autor: Joachim D. (Firma: JDCC) (scheppertreiber)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
W10 Webserver lokal, W10 Client mit FF (alles aktuell).

Die Rechteddatei (flat text) besteht aus Definitionen der
Benutzergruppen

group XXX {
  job (rechte)
  ...
}

user YYY {
  job (rechte)
  ...
}

etc. 72 Gruppen und knapp 15.000 User.

Der Krempel wird nach dem Laden der Rechte in entsprechende
Strukturen übertragen. Das Übertragen dauert so ewig. Im
Moment habe ich alles SSL-Zeugs raus und das Programm müßte
sich genauso verhalten als hätte ich das mit dem Watcom
kompiliert.

Die Übertragung funktioniert definitiv.

Also: Funktion ok aber halt strunzlahm ...

Autor: rµ (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Unter Linux wär ich der Geschichte mit strace, gprof und valgrind auf 
den Pelz gerückt.

Was genau dauert jetzt eigentlich 17s, so Aussagen wie "Das Übertragen 
dauert so ewig." klingen irgendwie danach als wär das Problem gar nicht 
richtig identifiziert.

Autor: Joachim D. (Firma: JDCC) (scheppertreiber)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
1. Öffnen und malloc() für Puffer
2. Laden in den Puffer
3. Entfernen aller CR, LFs
4. dann durch den String handeln und den Kram in die
   Strukuren laden

Punkt 4. schwächelt.

Die Größen der Strukuren gibt sizeof() gleich aus.

: Bearbeitet durch User
Autor: Jörg W. (dl8dtl) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Kannst du denn nicht mal ein Stück Beispielcode zitieren, ohne nun 
gleich allerlei Firmengeheimnisse preiszugeben?

Autor: rµ (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Joachim D. schrieb:
> 4. dann durch den String handeln und den Kram in die
>    Strukuren laden

Passieren da String -> Numerisch-Umwandlungen? Dunkel erinnere ich mich 
an eine Problem mit den Spracheinstellungen bzw. Locale-abhängiger 
Zahlenparserei die da jedesmal nachgeschaut hat ob jemand hinterrücks 
das Land geändert hat. Ist aber über 10 Jahre aus, dass ich mit dem Mist 
beschäftigen musste.

Autor: Joachim D. (Firma: JDCC) (scheppertreiber)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Pause ;)

Gleich kommt Besuch und ich muß noch die Schweinshaxn holen.
Am Sonntag geht weiter ;)))

Autor: Rolf M. (rmagnus)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Joachim D. schrieb:
> Windows 10.

Was für ein gcc ist das? mingw? cygwin? wsl?

Autor: Joachim D. (Firma: JDCC) (scheppertreiber)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
MinGW

Autor: cppbert3 (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Also die zp1 option ist schon mal ziemlich boese - warum global und 
nicht einfach nur auf den paar Structs die du serialisierst? Nicht 
boeser eher unschoen

Hast du die relevante funktion schon genau erkannt oder ist "irgendwie" 
der ganze code langsam - deine Posting erlauben keine klaren 
Rueckschluesse

Als profiler könntest du den VTune von intel verwenden - als teil vom 
parallel studio trial, der ist ziemlich einfach zu bedienen

Wie machst du deine Zeitmessung?

Autor: cppbert3 (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert

Autor: cppbert3 (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Und das packing kannst du auch mit dem watcom lokal kontrollieren: 
http://www.x-hacker.org/ng/wcppug/ng1963c.html

Wie machst du die globale zp1 entsprechung beim GNU gcc?
Lokal geht es so: 
https://gcc.gnu.org/onlinedocs/gcc-4.4.4/gcc/Structure_002dPacking-Pragmas.html

Welche Watcom version verwendest du 1.9 oder die V2 variante?
Gcc version? Moeglichkeit unter linux zu bauen - zum vergleichen?

Autor: Joachim D. (Firma: JDCC) (scheppertreiber)
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht lesenswert
Umgebung: Windows 10 Client und Serverm aktuell
httpd: Apache 2.4
MinGW: 6.30
Watcom: 1.9

Ich habe jetzt mal das Programm mit der Option -pg compiliert
und laufen lassen. Dann mit gprof den output lesbar gemacht.
Den etwas merkwürdigen output (der mir nicht weiterhilft, alles
0) hängt an.

Grüße Joe und schon vielen Dank !

Autor: mh (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Joachim D. schrieb:
> Den etwas merkwürdigen output (der mir nicht weiterhilft, alles
> 0) hängt an.

Uns hilft der auch nicht ;-)

Autor: Joachim D. (Firma: JDCC) (scheppertreiber)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Edit: beim lauf von gprof kamen jede Menge Meldungen
"BFD: Dwarf Error: Could not find abbrev number 84."
Was Zwerge damit zu tun haben .... ;)

Autor: Jörg W. (dl8dtl) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Joachim D. schrieb:
> Was Zwerge damit zu tun haben .... ;)

Diese nützlichen Zwerge verwalten die Debug-Informationen:

https://en.wikipedia.org/wiki/DWARF

Wenn die BFD-Bibliothek damit nicht klar kommt, dann ist sie wohl 
einfach mal zu alt für den Compiler.

Ich vermute allerdings eher, dass es irgendein Problem mit den 
(OS-)Timern bei dir gibt, die der Profiler-Code offenbar nicht benutzen 
kann, während die DWARF-Warnungen eher harmlos sind.

: Bearbeitet durch Moderator
Autor: Joachim D. (Firma: JDCC) (scheppertreiber)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Puh. Das könnte mir gprof auch sagen. Ergo Sackgasse.

Autor: Jörg W. (dl8dtl) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Joachim D. schrieb:
> Das könnte mir gprof auch sagen.

Dass die Timer dam Ende keine Zeit liefern, wird er wohl nicht wissen.

Ich hatte weiter oben mal nach einem minimalistischen Codebeispiel 
gefragt, kannst du sowas mal posten?

Autor: Joachim D. (Firma: JDCC) (scheppertreiber)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
        if ( ! LoadRightsD())
                return FALSE;
        jDspTime( NULL, "after load rights.d");

        // Kommentare, Tabs und NL durch Blanks ersetzen
        for ( p=fbuf; *p; p++){
                if ( *p == ';'){
                        for ( ; *p && *p != '\n'; p++){
                                *p = ' ';
                        }
                }
                if ( *p == '\n' ||  *p == '\r' ||  *p == '\t')
                        *p = ' ';
        }
        jDspTime( NULL, "after blank");

        for ( grpmax=usrmax=0, p=fbuf; *p; p++){
                *name = *workspace = 0;
                if ( memcmp( p, "group", 5) == 0){
                        p = scword( p); sscanf( p, "%s", name);
                        p = scword( p);
                        sscanf( p, "{%[^}]", workspace);
                        p += strlen( workspace) + 2;
                        GetGroup( name);
//                      jDspTime( NULL, "after get grp %s", name);
                }
                if ( memcmp( p, "user", 4) == 0){
                        p = scword( p); sscanf( p, "%s", name);
                        p = scword( p); sscanf( p, "{%[^}]", workspace);
                        p += strlen( workspace) + 2;
                        GetUser( name);
//                      jDspTime( NULL, "after get usr %s", name);
                }
        }
scword geht hinter den nächsten whitespace auf das nächste Wort.
In fbuf liegt die eingelesene Rechtdatei.
        static
        GetGroup(
        char    *name
        )
{
        char    *p, *q, *qq, job[256], parms[256];
        T_RJOB  *jp;
        int     i;

        strcpy( group[grpmax].id, name);

        // wenn in sys.cfg $timeout gesetzt ist dann als Vorgabe für
        // die Gruppe übernehmen

        if ( atoi( GetSysVar( "timeout"))){
                group[grpmax].timeout = atoi( GetSysVar( "timeout"));
//              JA( "timeout preset for grp '%s' set from sys.cfg: %d sec",
//                group[grpmax].id, group[grpmax].timeout);
        }

        p = workspace;
        if ( *p == ' ')
                p = scword( p);

        for ( ; *p; p = scword( p)){
                *job = *parms = 0;
                sscanf( p, "%s", job);
                if ( (q = strchr( job, '.')) != NULL){
                        *q = 0;
                        q++;
                } else {
                        q = job;
                }
                if ( strcmp( job, "isapo") == 0){
                        group[grpmax].isapo = TRUE;
                        continue;
                }
                if ( strcmp( job, "privat") == 0){
                        group[grpmax].privat = TRUE;
                        continue;
                }
                if ( strcmp( job, "testip") == 0){
                        group[grpmax].testip = TRUE;
                        continue;
                }
                if ( strcmp( job, "css") == 0){
                        p = scword( p);
                        sscanf( p, "(%[^)])", group[grpmax].css);
                        continue;
                }
                if ( strcmp( job, "timeout") == 0){
                        p = scword( p);
                        sscanf( p, "(%[^)])", parms);
                        group[grpmax].timeout = atoi( parms);
                        continue;
                }

                jp = NULL;
                for ( i=0; i<group[grpmax].jmax; i++){
                        if ( strcmp( group[grpmax].jobs[i].id, job) == 0){
                                jp = &group[grpmax].jobs[i];
                                break;
                        }
                }
                if ( jp == NULL){
                        jp = &group[grpmax].jobs[group[grpmax].jmax];
                        group[grpmax].jmax++;
                }
                strcpy( jp->id, job);

                p = scword( p);
                sscanf( p, "(%[^)])", parms);

                switch ( *q) {
                  case 'e':     // exec in docs ablegen
                  case 'd':
                        GetAccess( &jp->docs, parms);
                        break;
                  case 'n':
                        GetAccess( &jp->notes, parms);
                        break;
                  case 's':
                        if ( *(q+1) == 'c'){
                                strcpy( jp->scope, parms);
                                trim( jp->scope);
                        } else {
                                strcpy( jp->system, parms);
                        }
                        break;
                  case 'f':
                        StoreGrpFilter( jp, parms);
                        break;
                  default : ;
                }
                qq = p;
                if ( (p = strchr( p, ')')) == NULL){
                        JA( "*** missing ')' at group '%s' > '%s'", workspace, qq);
                        JSSayErr( "rights.d: missing ')' at group '%s' > '%s'", workspace, qq);
                        break;
                }
        }
        if ( ++grpmax >= GRPMAX){
                JSSayErr( "rights.d: Mehr als %d Gruppen definiert.", GRPMAX);
        }
        return 0;
}
        static
        GetUser(
        char    *name
        )
{
        char    *p, *q, *qq, tag[256], parms[256];

        strcpy( usr[usrmax].id, name);
        usr[usrmax].fmax = 0;
        p = workspace;
        if ( *p == ' ')
                p = scword( p);

        if ( atoi( GetSysVar( "timeout"))){
                usr[usrmax].timeout = atoi( GetSysVar( "timeout"));
//              JA( "timeout preset for usr '%s' set from sys.cfg: %d sec",
//                usr[usrmax].id, usr[usrmax].timeout);
        }

        for ( ; *p; p = scword( p)){
                *tag = *parms = 0;
                sscanf( p, "%s", tag);
                if ( strcmp( tag, "isapo") == 0){
                        usr[usrmax].isapo = TRUE;
                        continue;
                }
                if ( strcmp( tag, "privat") == 0){
                        usr[usrmax].privat = TRUE;
                        continue;
                }
                if ( strcmp( tag, "testip") == 0){
                        usr[usrmax].testip = TRUE;
                        continue;
                }
                p = scword( p);
                sscanf( p, "(%[^)])", parms);
                p += strlen( parms);
                trim( parms);
                if ( (p = strchr( p, ')')) == NULL){
                        JA( "*** missing ')' at '%s'", usr[usrmax].id);
                        JSSayErr( "rights.d: missing ')' at user '%s'", usr[usrmax].id);
                        break;
                }

                switch ( *tag) {
                  case 't': usr[usrmax].timeout = atoi( parms); break;
                  case 'm': strcpy( usr[usrmax].mail , parms); break;
                  case 'c': strcpy( usr[usrmax].css  , parms); break;
                  case 'a': strcpy( usr[usrmax].abt  , parms); break;
                  case 'n': strcpy( usr[usrmax].name , parms); break;
                  case 'p': strcpy( usr[usrmax].pass , parms); break;
                  case 'e': strcpy( usr[usrmax].expd , parms); break;
                  case 'g':
                        strcpy( usr[usrmax].group, parms);
                        break;
                  case 'f':
                        sscanf( parms, "%s%s%s",
                          usr[usrmax].filter[usr[usrmax].fmax].id,
                          usr[usrmax].filter[usr[usrmax].fmax].field,
                          usr[usrmax].filter[usr[usrmax].fmax].op);

                        q = scword( scword( scword( parms)));
                        if ( (qq = strstr( q, "hide")) != NULL){
                                memset( qq, ' ', 4);
                                usr[usrmax].filter[usr[usrmax].fmax].hide = TRUE;
                        }
                        trim( q);

                        strcpy( usr[usrmax].filter[usr[usrmax].fmax].expr, q);
                        usr[usrmax].fmax++;
                        break;
                  default : JA( "*** invalid entry %s\nworkspace: '%s'", tag, workspace);
                }
        }
        usrmax++;
        return 0;
}



: Bearbeitet durch Moderator
Autor: mh (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ich glaub nicht, dass Jörg W. das mit einem minimalistischen 
Codebeispiel gemeint hat. Ich tippe eher auf das was hier beschrieben 
wird: https://stackoverflow.com/help/minimal-reproducible-example

Der Vorteil ist, dass du beim Erstellen des Beispiels deinen Fehler 
selbst findest oder eine viel konkretere Frage stellen kannst.

Autor: Joachim D. (Firma: JDCC) (scheppertreiber)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Wenn ich da etwas rauswerfe wird es unklar. Ich weiß,
minimalistisch ist das eher nicht. Es muß halt eine
Kleinigkeit sein an der sich der GCC verschluckt.

Der Code ist seit min 10 in dieser Form aktiv und
funktioniert. Halt mit dem Watcom besser als mit dem GCC.

Autor: Frank M. (ukw) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Joachim D. schrieb:
> Mit GNU kompiliert: Laufzeit 17 sec (!)

Auch wenn Du den Lauf direkt danach nochmal startest? Wenn es beim 
zweiten Mal flotter geht, ist es der Virenscanner, der erstmal die neue 
EXE checkt.

Dasselbe Phänomen habe ich hier unter Windows auch, wenn ich mit mingw 
ein Windows-Programm erzeuge und danach starte.

Autor: leo (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Joachim D. schrieb:
> Der Code ist seit min 10 in dieser Form aktiv und
> funktioniert. Halt mit dem Watcom besser als mit dem GCC.

Kann es sein, dass du Uraltversionen der Tools einsetzt? Immerhin ist 
der Watcom ja auch mindestens 9 Jahre alt.

leo

Autor: Joachim D. (Firma: JDCC) (scheppertreiber)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Virenscanner ist nicht drauf.
gprof ist 2.28

Autor: mh (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Joachim D. schrieb:
> Wenn ich da etwas rauswerfe wird es unklar.

Unklarer als jetzt wirds nicht ;-)

Autor: cppbert3 (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
du solltest echt mehr mit Funktionen arbeiten, C++ und STL strings 
würden den Code wohl auch gehörig eindampfen

was bleibt:

1. liefert deinen gcc version auch exakt die gleichen Daten wie deine 
Watcom version? also keine weiteren Probleme ausser Geschwindigkeit

2. dein Beispiel so zusammen dampfen das man damit auch was anfangen 
kann
in dem Code sehe ich jetzt nichts schlimmes/komisches - aber es wäre
wohl am einfachsten wenn du deine lade-routine (von der du denkst das 
sie so langsam ist) in ein kleines Testprojekt extrahierst und damit 
Zeitmessungen machst

3. VTune von Intel (als Trial)

4. Visual Studio 2019 (Community) downloaden und integrierten Profiler 
nutzen: 
https://docs.microsoft.com/de-de/visualstudio/profiling/beginners-guide-to-performance-profiling?view=vs-2019

5. mal unter Linux kompilieren: falls du eins da hast (LiveUSB Stick 
Ubuntu 19.10?) - da hast du dann gcc und gprof in neu und native

was willst du machen?

Autor: Joachim D. (Firma: JDCC) (scheppertreiber)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Zu 1: Ja, ich habe kontrolliert was er da eingelesen hat

Zu 2: das ist etwas problematisch. Es ist halt ziemlich umfangreich.

Zum Ausprobieren habe ich das alles mal direkt in main()
unmittelbar nach dem Programmstart
aufgerufen ohne das ganze Beiwerk. Es ändert sich nichts.

Zu 3: Noch mehr tools ? Ich denke, es ist kein tuning Problem.

zu 4: MinGW legt das im Unix-Format ab, da kann M$ angeblich
nichts mit anfangen

zu 5: Ich habe irgendwo noch einen älteren Rechner mit debian.
Es muß aber in der Firma unter W10 laufen.

Autor: cppbert3 (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Joachim D. schrieb:
> Zu 1: Ja, ich habe kontrolliert was er da eingelesen hat

schon mal gut

> Zu 2: das ist etwas problematisch. Es ist halt ziemlich umfangreich.

aber wäre schon gut - falls es sich (noch) lohnt

> Zum Ausprobieren habe ich das alles mal direkt in main()
> unmittelbar nach dem Programmstart
> aufgerufen ohne das ganze Beiwerk. Es ändert sich nichts.

ich wuerde das per #define da mal belassen und nur damit testen - 
einfach ein kleineres Szenario

> Zu 3: Noch mehr tools ? Ich denke, es ist kein tuning Problem.

das wohl nicht aber es ist ja auch noch nicht klar welche deiner(oder 
der Standard-lib) Funktionen plötzlich einen Faktor 170 langsamer sind, 
oder?

https://software.intel.com/en-us/vtune/choose-download#standalone

Es geht nicht um tuning sondern um das eingrenzen - du hast gemeint 
time(NULL) liefert 0 oder so was - kannst du schon genau sagen welche 
Routine so langsam ist?

> zu 4: MinGW legt das im Unix-Format ab, da kann M$ angeblich
> nichts mit anfangen

deswegen auch mit VS2019 bauen und profilen - dann ist alles im M$ 
Format

https://visualstudio.microsoft.com/de/vs/

> zu 5: Ich habe irgendwo noch einen älteren Rechner mit debian.
> Es muß aber in der Firma unter W10 laufen.

es macht nur Sinn von du was aktuelles hast, ansonsten liegt da ein 
uralt gcc drauf und keiner kann helfen und es artet einfach nur aus und 
kostet Zeit - am einfachsten ein Ubuntu 19.10 vom USB-Stick booten und 
dann gcc/gprof usw. installieren - aber nur wenn du dich aus kennst(und 
sonst nix hilft): 
https://www.thomas-krenn.com/de/wiki/Ubuntu_von_einem_USB_Stick_installieren 
(oder einfach nur als Live-System booten ohne installation) - USB-Stick 
muss gross genug sein

Autor: Joachim D. (Firma: JDCC) (scheppertreiber)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
GetUsr() wird 14371 mal aufgerufen, also mal mit clock()
die Ticks gezählt. Alles 0, 22mal 16 ??? Eigentlich sind
die user bis auf die Kundennummer, PW und Zugriffsrechte
gleich, Wird immer doller.

Autor: Jörg W. (dl8dtl) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Was passiert, wenn du mal
#define GetSysVar(foo) "42"

davor setzt?

Dieses sich wiederholende
atoi(GetSysVar("timeout"))

stößt mir irgendwie auf. Solch eine String->Int-Konvertierung macht man 
doch am besten nur genau einmal beim Systemstart.

Autor: cppbert3 (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
>GetUsr() wird 14371 mal aufgerufen,

erwartest du das?

>also mal mit clock() die Ticks gezählt. Alles 0, 22mal 16 ???

was soll das bedeuten - 22mal 16?

>Eigentlich sind die user bis auf die Kundennummer,
>PW und Zugriffsrechte gleich,

ich verstehe den Bezug zur Geschwindigkeit nicht

>Wird immer doller.

also verhält sich dein System doch nicht so ganz korrekt?

es könnte problemlos möglich sein das du dich seit 10 Jahren auf 
undefiniertes Verhalten verlässt, oder die 10 Jahre (oder älter) stdlib 
vom Watcom 1.9 Fehler besser verkraftet oder ignoriert usw.
und der gcc nebst anderer stdlib macht da eben was völlig anderes - das 
passiert sehr häufig - deswegen immer schön multi-compiler Builds 
machen, damit man genau so was erkennt - deswegen auch VS2019 oder 
Linux/gcc zum vergleichen

mal
http://cppcheck.sourceforge.net/ - open source, klein und schnell
und
PVS Studio Trial (der Gründliche) über den Code laufen lassen?
https://www.viva64.com/en/pvs-studio/

Autor: cppbert3 (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
@Jörg W.

>if ( atoi( GetSysVar( "timeout"))){
>  usr[usrmax].timeout = atoi( GetSysVar( "timeout"));

also 14371 x 2 = 28742 Aufrufe von GetSysVar+atoi

es könnte sein das der Watcom das (fälschlicher weise) nicht als 
volatile erachtet und dann "nur" die 14371 Aufrufe macht

ich könnte mir gut vor stellen das der Watcom Kompiler da einige 
Geschwindigkeits-steigernden aber nicht korrekte Dinge macht

Autor: Vlad T. (vlad_tepesch)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Joachim D. schrieb:
> ...

Auf jeden Fall solltest du dringed deinen Code refactorn.
Das ist ja furchtbar. Da blickt ja keiner durch.

diese unsägliche  scword( p) / sscanf orgien.

es gibt eine C-Funktion, die Zerlegt dir einen String in Token:
   char *token;
   const char s[] = " ";

   /* get the first token */
   token = strtok(str, s);
   
   /* walk through other tokens */
   while( token != NULL ) {
      printf( " '%s'\n", token );
    
      token = strtok(NULL, s);
   }



Aber ich würde eher c++, streams und reguläre ausdrücke benutzen oder 
gleich ne kleine sqlite-Datenbank verwenden.



Was performance angeht:
Ansonsten ist mir das was Jörg gefunden hat auch aufgestoßen:
Jörg W. schrieb:
> GetSysVar





cppbert3 schrieb:
>>also mal mit clock() die Ticks gezählt. Alles 0, 22mal 16 ???
>
> was soll das bedeuten - 22mal 16?

Ich vermute, dass die Auflösung von clock 16ms ist und er bei den 14371 
Funktionsaufrufen 22 mal einen Taskswitch gemessen hat.

: Bearbeitet durch User
Autor: Joachim D. (Firma: JDCC) (scheppertreiber)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
cppbert3 schrieb:
> also 14371 x 2 = 28742 Aufrufe von GetSysVar+atoi

ausgelagert. Immer noch 16,2 sec.

Autor: cppbert3 (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
nutzt den QueryPerformance-Counter und verteile mal ein bisschen 
Benchmark Code damit du mal die Funktion findest die verantwortlich ist

https://stackoverflow.com/questions/1739259/how-to-use-queryperformancecounter

Autor: Joachim D. (Firma: JDCC) (scheppertreiber)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Vlad T. schrieb:
> Aber ich würde eher c++, streams und reguläre ausdrücke benutzen oder
> gleich ne kleine sqlite-Datenbank verwenden.

Das ist halt das System ohne SQLite. Erheblich schneller
wie das "mit" (selbstgebaute DB).

C++ nur im Notfall, wozu in einer Rechnedatei reguläre Ausdrücke ?

Autor: Vlad T. (vlad_tepesch)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Joachim D. schrieb:
> Vlad T. schrieb:
>> Aber ich würde eher c++, streams und reguläre ausdrücke benutzen oder
>> gleich ne kleine sqlite-Datenbank verwenden.
>
> Das ist halt das System ohne SQLite. Erheblich schneller
> wie das "mit" (selbstgebaute DB).
>
> C++ nur im Notfall, wozu in einer Rechnedatei reguläre Ausdrücke ?

du willst mir erzählen, dass du schneller dateien lädst und nach usern 
und so durchsuchst, als sqlite?


wozu c++?
lesbarer, leichter wartbarer Code, da es eine vernünftige Lib mitbringt 
und nicht alles selbst geschrieben werden muss.
Ist mit großer Wahrscheinlichkeit auch schneller, als das ganze scanf, 
da die Typen zur Kompile-Zeit bekannt sind und nicht erst ein 
Formatstring geparst werden muss.

warum reguläre ausdrücke?
Na zum Parsen der Datensätze.
Aber da weiß ich zu wenig über deinen Aufbau, ob es den Code 
übersichtlicher oder komplizierter machen würde.

: Bearbeitet durch User
Autor: cppbert3 (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
>C++ nur im Notfall, wozu in einer Rechnedatei reguläre Ausdrücke ?

weil wir das Format nicht kennen und solche "{%[^}]" sscanf Aufrufe 
vorkommen - aber ich glaube auch nicht das man hier reguläre Ausdrücke 
braucht die Rechte-Datei ist bestimmt sehr einfach zu lesen

Autor: cppbert3 (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
>du willst mir erzählen, dass du schneller dateien lädst und nach usern
>und so durchsuchst, als sqlite?

ich glaube das wird alles einmal ein gelesen und fertig, die suchen 
drauf werden wohl minimal/trivial sein

Autor: Joachim D. (Firma: JDCC) (scheppertreiber)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
sqlite ist recht flott.

Es kommt halt eine seine Grenzen wenn da mal 100 Mio Records
zu testen sind. Ich komme da auf 0,1 sec ;)

Man muß auch nicht immer mit Kanonen auf Spatzen ...

Autor: Joachim D. (Firma: JDCC) (scheppertreiber)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Falls das noch nicht durch ist:

Es ist ein CGI-Programm. Der Indianer ruft das auf und möchte
natürlich so schnell wie möglich Ergebnisse sehen ...

sqlite kann ich über die DLL einbinden und verwenden. Das
dauert halt ein wenig.

Autor: Joachim D. (Firma: JDCC) (scheppertreiber)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
strtok zerstört mir den Quellstring. Gerade beim Einlesen
von Wertepaaren nicht so toll.

Autor: mh (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Joachim D. schrieb:
> sqlite ist recht flott.
>
> Es kommt halt eine seine Grenzen wenn da mal 100 Mio Records
> zu testen sind. Ich komme da auf 0,1 sec ;)
>
> Man muß auch nicht immer mit Kanonen auf Spatzen ...

Das ist das ideale Einsatzgebiet für sqlite.

Autor: cppbert3 (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Er wird es jetzt aber nicht auf sqlite portieren und sooo schlimm sieht 
der code nun auch nicht ohne das ganze aus

die Frage bleibt: was genau frisst Zeit

sind es nur die 3 Routinen die du gezeigt hast welche die 17sek 
schlucken?

hast du jetzt mit dem VTune oder VS2019 getestet oder wie ist dein 
Stand?

Autor: Joachim D. (Firma: JDCC) (scheppertreiber)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Es sind nur die Teile die ich gepostet hatte. In der
Schleife bleiben die 16 sec liegen.

VTune werde ich mir mal anschauen.

Mir qualmt jetzt der Schädel - morgenm wieder ;)

Autor: Vlad T. (vlad_tepesch)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Joachim D. schrieb:
> sqlite kann ich über die DLL einbinden und verwenden. Das
> dauert halt ein wenig.

man kann aber auch das amalgamation source file einfach mitkompilieren, 
dann entfällt der DLL-Ladeprozess und er linkt nur nimmt nur das mit, 
was du auch brauchst.

Joachim D. schrieb:
> strtok zerstört mir den Quellstring. Gerade beim Einlesen
> von Wertepaaren nicht so toll.

wieso?
einmal Input parsen und dann vergessen, was juckt dich, ob da ein paar 
'\0' eingestreut werden.
Ist doch sogar gut, dann können die substrings direkt verwendet werden 
und müssen nicht erst kopiert werden (es sei denn, du willst sie über 
die Lebenszeit des Inputpuffers hinaus behalten)

Joachim D. schrieb:
> Es kommt halt eine seine Grenzen wenn da mal 100 Mio Records
> zu testen sind. Ich komme da auf 0,1 sec ;)

Ist eventuell eine Frage der gewählten Indexe - dazu ist über deine 
Struktur zu wenig bekannt.

Joachim D. schrieb:
> Man muß auch nicht immer mit Kanonen auf Spatzen ...
sqlite ist keine Kanone und (d)eine User-Datenbank kein Spatz, zumal du 
mindestens 2 Tabellen mit mindestens einer Relation zwischen Benutzer 
und Gruppen hast, eventuell auch noch eine Zwischen Gruppen und 
Bereichen, auf die diese Gruppen zugriff haben.

Und mit >14000 Userneinträgen sind die ja auch gut gefüllt.

Autor: cppbert3 (Gast)
Datum:

Bewertung
1 lesenswert
nicht lesenswert
es wäre schön wenn du aus den 3 Funktionen einen kompilierbares Beispiel 
machen könntest - also auch mit den Hilfs-Funktionen und das posten - 
vielleicht sogar mit einer gleich großen Rechte-Datei mit Fake-Daten und 
einer Format-Beschreibung :)

Autor: Vlad T. (vlad_tepesch)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
cppbert3 schrieb:
> es wäre schön wenn du aus den 3 Funktionen einen kompilierbares Beispiel
> machen könntest - also auch mit den Hilfs-Funktionen und das posten -
> vielleicht sogar mit einer gleich großen Rechte-Datei mit Fake-Daten und
> einer Format-Beschreibung :)

das wär super, dann könnte man eine kleine challange draus machen, wer 
den fixesten Parser schreibt.

Wobei ich mich immer noch am meisten wundere, wo die 16s für so ein 
Bissel Inputparsing herkommen.

Autor: cppbert3 (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
>Wobei ich mich immer noch am meisten wundere,
>wo die 16s für so ein
>Bissel Inputparsing herkommen.

so weit ich das verstehe ist alles als ein grosser String verfügbar
d.h. es bleiben nur sscanf, memcmp, strcmp, scword, strlen, GetSysVar, 
GetAccess, StoreGrpFilter und atoi oder wild rennende Schleifen sonst 
ist da ja nichts

Autor: mh (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Vlad T. schrieb:
> Wobei ich mich immer noch am meisten wundere, wo die 16s für so ein
> Bissel Inputparsing herkommen.

Sind "wir" uns denn sicher, dass die 16s auch wirklich da anfallen?

Autor: cppbert3 (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ich noch nicht ganz

@Joachim D.

du musst deutlich sagen was du schon getestet hast und wie
Benchmarking mit der Stopuhr oder sind das diese jDspTime Aufrufe?

Autor: Vlad T. (vlad_tepesch)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
sehr ungeschickt finde ich die Verwendung der Globalen Variable 
workspace, die einen Pointer auf den gesamten String bekommt.

Die Tests auf ein missing ')' sind quasi wirkungslos, weil abgesehen vom 
letzten "Datenbank"eintrag wird es in dem 4MB-String immer noch irgendwo 
eine Klammer geben.
Damit hier nennenswerte Zeiten zustande kommen, müsste aber in einem 
großem Kontinuierlichem Bereich die Klammern fehlen, aber trotzdem 
unschön.

Ich würde entweder meine Datei so definieren, dass pro Zeile ein 
Datensatz, und dann die Zeilen unabhängig voneinander prozessieren.

Oder zumindest sauber die Endtokens extrahieren und in den 
Datensatz-Parsern sauber abprüfen.

@OP:

Bei welchem C-Compiler ist das eigentlich eine (legale) 
Funktionsdefinition (, die keine Warnung wirft):
static
GetUser(
         char    *name
        )
{
?

Hast du dir mal deine Compilerwarnungen angeschaut?

Ich würde die So definieren:
static BOOL ParseUser(User* o_user, const char* i_start, const char* i_end);
(das Extrahieren des Namens gehört für mich auch nach drin)

und wenn in drin irgend welche indices bestimmt werden, immer testen, ob 
ich über das Ende der aktuellen Definition gelaufen bin.


Eine andere Sache:
sind die Strukturen denn vorher genullt?
>  for ( i=0; i<group[grpmax].jmax; i++){
jmax wurde vorher sonst noch nicht gesetzt oder so.

Das Itertieren und strcmp könnte sonst unter umständen recht lange 
dauern und müsste, je nach Größe deiner Arrays und jmax Datentyp, auch 
nicht unbedingt eine Zugriffsverletzung geben.

Das ist aktuell mein heißester Kandidat.

: Bearbeitet durch User
Autor: Joachim D. (Firma: JDCC) (scheppertreiber)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ich teste das morgen erstmal in Ruhe durch.

Irgendwo muß aber ein Unterschied beider Compiler sein.
Den suche ich.

Autor: foobar (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Btw, möglicher read-past-end-of-buffer (fbuf=";") in
        // Kommentare, Tabs und NL durch Blanks ersetzen
        for ( p=fbuf; *p; p++){
                if ( *p == ';'){
                        for ( ; *p && *p != '\n'; p++){
                                *p = ' ';
                        }
                }
                if ( *p == '\n' ||  *p == '\r' ||  *p == '\t')
                        *p = ' ';
        }

Wie sieht scword aus?

Autor: Joachim D. (Firma: JDCC) (scheppertreiber)
Datum:

Bewertung
-1 lesenswert
nicht lesenswert
char *
        scword(
        char     *P
        )
{
        unsigned char *p;

        p = (unsigned char*) P;
        if ( isspace( *p)){
                for ( ; isspace( *p); p++) ;
        } else {
                for ( ; *p  && ! isspace( *p); p++) ;
                for ( ; isspace( *p); p++) ;
        }
        return p;
}

: Bearbeitet durch Moderator
Autor: foobar (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
scanf und isspace sind beide Locale-abhängig.  Hast du schon 
sichergestellt, dass dir das nicht dazwischenfunkt?

> Irgendwo muß aber ein Unterschied beider Compiler sein.

Kann auch in den Libraries sein ...

Autor: Joachim D. (Firma: JDCC) (scheppertreiber)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
        for ( grpmax=usrmax=0, p=fbuf; *p; p++){
                *name = *workspace = 0;
                if ( memcmp( p, "user", 4) == 0){
                        p = scword( p); // sscanf( p, "%s", name);
                        p = scword( p); sscanf( p, "{%[^}]", workspace);
                        p += strlen( workspace) + 2;
                }
        }
Der Rest ist auskommenttiert.

Laufzeit ist 7,9 sec, wenn ich den Kommentar vor dem sscanf
entferne die übliche 15 sec. Deutet auf das sscanf hin.

Wenn ich mit setlocale( LC_ALL, NULL) das locale abrufe
erhalte ich "C". Ich suche noch den Schlauch auf dem
ich stehe. Die Rechtedatei enthält neben ASCII auch deutsche
Umlaute, ich kann mir aber nicht vorstellen, daß das einen
Einfluß haben könnte.

Autor: cppbert3 (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
>Deutet auf das sscanf hin.

ich kann mir kaum vorstellen das die alte sscanf implementierung vom 
Watcom so viel toller ist als die vom gcc - aber es scheint ja so zu 
sein

Autor: Joachim D. (Firma: JDCC) (scheppertreiber)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Liegt irgendwie auf der Hand ... ;(

Autor: Joachim D. (Firma: JDCC) (scheppertreiber)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Mein erster C-Compiler Manx Aztek konnte nur das 'C'-locale,
der Watcom (glaube ich) auch. Beim GCC ist das anscheinend
etwas komplexer.

Autor: cppbert3 (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
bau doch mal das sscanf %s Verhalten als eigene Funktion

sollte ja nicht so schwer sein:

sscanf %s: String of characters. This will read subsequent characters 
until a whitespace is found (whitespace characters are considered to be 
blank, newline and tab).

Autor: mh (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
cppbert3 schrieb:
> aber es scheint ja so zu sein

Es ist immer noch wahrscheinlicher, dass Joachim nen Bug in seinem 
Programm hat.

Was ist aus dem struct-Alignment Problem geworden? Was ist mit dem 
time(NULL) == 0 geworden? Funktioniert gprof mittlerweile? Braucht das 
sscanf die Zeit oder etwas anderes? Wird es nur vermutet, weil es zum 
Programm hinzufügt wird? Was ist mit den Rückgabewerten von sscanf?

Autor: Joachim D. (Firma: JDCC) (scheppertreiber)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
sscanf gibt immer 1 zurück.

Es gibt keine Programme die bugfrei sind. In diesem Fall ist
ein Bug vor diesem Aufruf der sscanf abschießt unwahrscheinlich
(unmittelbar in main() ganz oben rufe ich das auf).

gprof produziert schicht Unsinn.
sizeof zeigt mir bei den verschachtelten Strukturen unter GCC
und Watcom die gleiche Größe, das sollte passen.

Autor: mh (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Da du ja nicht bereit bist ein minimal funktionierendes Programm mit dem 
Fehler zu erstellen, kannst etwas anderes probieren. Schreibe ein 
Programm, das deine Datei öffnet und dann einfach jedes Wort "%s" in der 
Datei liest. Überprüfe deine Rückgabewerte und achte darauf, dass deine 
Buffer groß genug sind. Zeige das Programm hier und teste es an deiner 
Datei.

Autor: Eric B. (beric)
Datum:

Bewertung
1 lesenswert
nicht lesenswert
Joachim D. schrieb:
> char *
>         scword(
>         char     *P
>         )
> {
>         unsigned char *p;
>
>         p = (unsigned char*) P;
>         if ( isspace( *p)){
>                 for ( ; isspace( *p); p++) ;

Hier hast du vll einen Überlauf, wenn *p == \000.

Ansonsten:
char *scword(char *P) {
  char *p = P;
  
  // skip non-space
  while (*p && !isspace(*p)) {
    p++;
  }

  // skip space 
  while (*p && isspace(*p))  {
    p++;
  }

  return p;
}
Macht das gleiche, aber mMn übersichtlicher und ohne Überlauf :-)

Autor: Rolf M. (rmagnus)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Eric B. schrieb:
> Hier hast du vll einen Überlauf, wenn *p == \000.

Warum? Wenn *p == '\0', dann gibt iusspace(*p) 0 zurück (schließlich ist 
'\0' kein Space), und der if-Zweig wird gar nicht erst angesprungen.
Dass man die Verzweigung ganz weglassen kann, da gebe ich dir recht.

> char *p = P;

Da ist das unsigned verloren gegangen.

: Bearbeitet durch User
Autor: Joachim D. (Firma: JDCC) (scheppertreiber)
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht lesenswert
Dawai. Alle benutzten Funktionen sind da drinnen.

Laufzeit immer noch 16 sec

load rights.d: ellapsed time 0.3100 sec
after blank: ellapsed time 0.3250 sec
after load grp: ellapsed time 16.7340 sec

: Bearbeitet durch User
Autor: foobar (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
> Hier hast du vll einen Überlauf, wenn *p == \000.

Der Kode ist nicht optimal, korrekt ist er aber.  isspace(0) ist 0.

Deiner mag kürzer sein, leider ist er aber falsch ;-)  (Hint, der 
unsigned char pointer ist wichtig!)


Da eh vorher alle Whitespaces nach ' ' gewandelt werden, wäre *p==' ' 
ein guter Ersatz für das isspace(*p) (und dann würde dein Kode auch 
funktionieren).

Autor: cppbert3 (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Joachim D. schrieb:
> Dawai. Alle benutzten Funktionen sind da drinnen.
>
> Laufzeit immer noch 16 sec
>
> load rights.d: ellapsed time 0.3100 sec
> after blank: ellapsed time 0.3250 sec
> after load grp: ellapsed time 16.7340 sec

jetzt brauchen wir noch eine Datei zum testen - kannst du was 
generieren?

Autor: rµ (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Die Spannung steigt. Testdaten würd man noch brauchen.

Autor: cppbert3 (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
dein #pragma pack(1) ist soooo fiese, aus alter Gewohnheit?
wofür brauchst du das überhaupt - das wirkt sich doch nur auf structs 
aus?

mach die Packs wenn noetig IMMER nur um die structs die das wirklich 
brauchen - niemals Programmweit - das kann dir die Performanz so in den 
Keller drücken, und, und, und

Autor: cppbert3 (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
gut nicht unbedingt die Performanz, aber trotzdem ist es nie schön 
global alignment zu forcieren - lass den Kompiler machen wie er es fuer 
sinnvoll hält - auf einem ARM oder Sparc System wäre das schon lange 
nicht mehr portabel - und unnötig

Autor: Yalu X. (yalu) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ohne das jetzt im Detail durchgespielt zu haben, würde ich sagen, dass 
da über das Ende von fbuf hinaus gelesen wird, da p gleich an mehreren 
Stellen in der Schleife erhöht wird.

Autor: Joachim D. (Firma: JDCC) (scheppertreiber)
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht lesenswert
cppbert3 schrieb:
> jetzt brauchen wir noch eine Datei zum testen - kannst du was
> generieren?

Die sollte ich erst anonymisieren. Dauert ein wenig ...

structure alignment brauche ich hier eigentlich nicht. Eher bei
binären Daten wo die Strukturen hintereinander in einer Datei liegen.
Ohne #pragma gleiche Laufzeit.

Yalu X. schrieb:
> Ohne das jetzt im Detail durchgespielt zu haben, würde ich sagen, dass
> da über das Ende von fbuf hinaus gelesen wird, da p gleich an mehreren
> Stellen in der Schleife erhöht wird.

Das paßt.


loaded 3504875
load rights.d: ellapsed time 0.3160 sec
after blank: ellapsed time 0.3460 sec
p      00E17B0C
p-fubf 3504876
after load grp: ellapsed time 16.8970 sec

Autor: foobar (Gast)
Datum:

Bewertung
1 lesenswert
nicht lesenswert
> Dawai. Alle benutzten Funktionen sind da drinnen.

Das schaut sehr ach einem langsamen sscanf oder isspace aus.  Bevor ich 
mich damit rumschlagen würde, würd ich die durch was eigenes ersetzen - 
sscanf ist eh ne Krankheit ;-)

Allerdings sind in deinem Programm mind drei Fehler drin.  Beide 
for-Schleifen können das Ende des Strings verpassen und wühlen dann wild 
im Speicher: p wird innerhalb des Bodys evtl auf das \0 oder gar drüber 
hinaus gesetzt und das p++ geht dann zu weit.  Außerdem ist 
memcmp(a,b,n) nur gültig, wenn a und b beide noch mind n Bytes haben; 
bei dir ist das für a am Ende des Strings nicht gegeben (evtl segfault). 
Ich denke nicht, dass das hier das Problem ist, wollte dich nur drauf 
hinweisen.

Autor: Joachim D. (Firma: JDCC) (scheppertreiber)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Danke foobar,

ich schaue mir das nachher nochmal genauer an. Ich muß jetzt
mal 2 h weg ;(.

Irgendwie wird das richtig spannend ...

Autor: Eric B. (beric)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Rolf M. schrieb:
> Eric B. schrieb:
>> Hier hast du vll einen Überlauf, wenn *p == \000.
>
> Warum? Wenn *p == '\0', dann gibt iusspace(*p) 0 zurück (schließlich ist
> '\0' kein Space), und der if-Zweig wird gar nicht erst angesprungen.

Nicht beim if, sondern in der for-schleife; da wird nicht mehr auf 0 
getestet.

Rolf M. schrieb:
>> char *p = P;
>
> Da ist das unsigned verloren gegangen.

"left as excercise for the reader" ;-P

Autor: Joachim D. (Firma: JDCC) (scheppertreiber)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
isspace( *p) durch *p == ' ' ersetzt, keine Änderung

Autor: cppbert3 (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Joachim D. schrieb:
> Die sollte ich erst anonymisieren. Dauert ein wenig ...

das wäre perfekt

Autor: cppbert3 (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Joachim D. schrieb:
> Ohne #pragma gleiche Laufzeit.

hat in deinem Beispiel ja auch keine Auswirkung

Autor: rµ (Gast)
Datum:

Bewertung
1 lesenswert
nicht lesenswert
https://stackoverflow.com/questions/23923924/why-is-glibcs-sscanf-vastly-slower-than-fscanf-on-linux

Falls das MinGW das auch so macht passiert bei jedem sscanf ein strlen 
auf den string, das schaut also jeden Schleifendurchlauf den gesamten 
Puffer an.

Autor: Eric B. (beric)
Datum:

Bewertung
1 lesenswert
nicht lesenswert
Eigentlich kann man das eindampfen auf:
char *scword(unsigned char *p) {
  for ( ; *p && !isspace(*p); p++) ;
  for ( ; *p &&  isspace(*p); p++) ;
  return p;
}

Autor: Rolf M. (rmagnus)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Eric B. schrieb:
> Rolf M. schrieb:
>> Eric B. schrieb:
>>> Hier hast du vll einen Überlauf, wenn *p == \000.
>>
>> Warum? Wenn *p == '\0', dann gibt iusspace(*p) 0 zurück (schließlich ist
>> '\0' kein Space), und der if-Zweig wird gar nicht erst angesprungen.
>
> Nicht beim if, sondern in der for-schleife; da wird nicht mehr auf 0
> getestet.

Da gilt das gleiche wie für das if. Die Schleife bricht ab, sobald das 
Zeichen kein Space ist, also auch beim '\0'.

foobar schrieb:
> Allerdings sind in deinem Programm mind drei Fehler drin.

Ich hätt noch einen:
Wenn der String, der nach name oder workspace geschrieben wird, mal 
länger als 255 Zeichen sein sollte, gibt's einen Buffer Overflow. Ich 
würde dem sscanf_Formatstring noch die maximale Länge mitteilen.

Autor: rµ (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
rµ schrieb:
> 
https://stackoverflow.com/questions/23923924/why-is-glibcs-sscanf-vastly-slower-than-fscanf-on-linux
>
> Falls das MinGW das auch so macht passiert bei jedem sscanf ein strlen
> auf den string, das schaut also jeden Schleifendurchlauf den gesamten
> Puffer an.

Das hat also ~n^2/2 Zeichenvergleiche zur Folge, haut die Caches des 
Prozessors zusammen, das spürt man schon bei entsprechendem n.

Autor: foobar (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Mit so nen Scheiß mußte sich Watcom noch nicht rumschlagen:
# echo $LANG
en_US.UTF-8
# echo ä | grep '[ab]'
# echo ä | grep '[a-b]'
ä
# echo ä | LANG=C grep '[a-b]'
#

Wenn du die Libraryersteller den "LANG=C"-Fall nicht ordentlich 
optimiert haben, geht die Laufzeit der Stringroutinen schnell gegen 
Unbrauchbar.  Selbst mit Optimierung ist es gern langsamer als zu 
Watcom-Zeiten.

Autor: A. K. (prx)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Joachim D. schrieb:
> Mit Watcom kompiliert: Laufzeit 100 msec
> Mit GNU kompiliert: Laufzeit 17 sec (!)

Wie lange braucht dein Programm auf GNU, wenn in main() nichts 
drinsteht? Nicht dass da bloss die Startzeit eines leeren Programms in 
einer verhunzten Umgebung gemessen wird.

Autor: foobar (Gast)
Datum:

Bewertung
1 lesenswert
nicht lesenswert
rµ schrieb:
> 
https://stackoverflow.com/questions/23923924/why-is-glibcs-sscanf-vastly-slower-than-fscanf-on-linux
>
> Falls das MinGW das auch so macht passiert bei jedem sscanf ein strlen
> auf den string, das schaut also jeden Schleifendurchlauf den gesamten
> Puffer an.

Good shot!

Autor: Kaj (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Rolf M. schrieb:
> Ich hätt noch einen:
> Wenn der String, der nach name oder workspace geschrieben wird, mal
> länger als 255 Zeichen sein sollte, gibt's einen Buffer Overflow. Ich
> würde dem sscanf_Formatstring noch die maximale Länge mitteilen.
Das sagt auch CppCheck:
[/home/ghost/devel/c_test/main.c:63] (warning) sscanf() without field width 
limits can crash with huge input data. Add a field width specifier to fix 
this problem.

Sample program that can crash:

#include <stdio.h>
int main()
{
    char c[5];
    scanf("%s", c);
    return 0;
}

Typing in 5 or more characters may make the program crash. The correct usage 
here is 'scanf("%4s", c);', as the maximum field width does not include the 
terminating null byte.
Source: http://linux.die.net/man/3/scanf
Source: http://www.opensource.apple.com/source/xnu/xnu-1456.1.26/libkern/stdio/scanf.c [invalidscanf]

[/home/ghost/devel/c_test/main.c:64] (warning) sscanf() without field width 
limits can crash with huge input data. Add a field width specifier to fix 
this problem.

Autor: cppbert3 (Gast)
Datum:

Bewertung
1 lesenswert
nicht lesenswert
>char *scword(unsigned char *p) {
>  for ( ; *p && !isspace(*p); p++) ;
>  for ( ; *p &&  isspace(*p); p++) ;
>  return p;
>}

viel schöner geht nicht :)

Autor: Rolf M. (rmagnus)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Soweit ich sehe, nutzt MinGW die Microsoft-Implementation von sscanf. 
Der Watcom-Compiler hat vielleicht eine eigene.

In diesem Thread sind Zahlen zu finden, die darauf hindeuten, dass es 
tatsächlich einfach an einem Elends langsamen sscanf unter Windows 
liegen könnte:
http://www.cplusplus.com/forum/general/261127/

$ cl /EHsc bar.cpp
$ bar.exe
C fgets, 5242880 records, 2.00438 seconds
C fgets+sscanf, 5242880 records, 13.4581 seconds

Autor: rµ (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
rµ schrieb:
> Das hat also ~n^2/2 Zeichenvergleiche zur Folge, haut die Caches des
> Prozessors zusammen, das spürt man schon bei entsprechendem n.

aaargl, er macht das sogar 2mal pro Durchlauf.

Ersatz vom sscanf im Beispielprogramm ist ja trivial, damit sollte sich 
feststellen lassen obs daran liegt.

Falls das reale Programm sscanf für mehr benutzt muss man halt ein wenig 
umorganisieren, z.b. so, dass jeglicher whitespace (modulo del {}) durch 
\0 ersetzt wird, das Ende des Puffers muss man dann halt anders 
erkennen. Damit würde der strlen overhead wegfallen. Falls es das ist.

Autor: Joachim D. (Firma: JDCC) (scheppertreiber)
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht lesenswert
loaded 3504875
load rights.d: ellapsed time 0.3100 sec
after blank: ellapsed time 0.3200 sec
p      00E02B73
p-fubf 3504979
after load grp: ellapsed time 3.8760 sec

das zweite sscanf durch was anderes ersetzt.

Autor: Joachim D. (Firma: JDCC) (scheppertreiber)
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht lesenswert
kein sscanf mehr:


loaded 2325000
load rights.d: ellapsed time 0.2290 sec
after blank: ellapsed time 0.2390 sec
p      00E7CA28
p-fubf 2325000
after load grp: ellapsed time 0.2440 sec

rights.d ist die zu parsende Datei (mit Stuß).

Autor: cppbert3 (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
dann bleibt dir nur die scanfs aus zu tauschen

Autor: Yalu X. (yalu) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Joachim D. schrieb:
> Yalu X. schrieb:
>> Ohne das jetzt im Detail durchgespielt zu haben, würde ich sagen, dass
>> da über das Ende von fbuf hinaus gelesen wird, da p gleich an mehreren
>> Stellen in der Schleife erhöht wird.
>
> Das paßt.

Ich hab's mir gerade mal genauer angeschaut:

Das Problem tritt nur dann auf, wenn die Datei direkt nach dem letzten
'}' endet. Da darauf vermutlich immer noch ein CR+LF folgt, wird das
Ende-'\0' nicht übersehen. Ich würde das aber trotzdem ändern.

Der Grund für die lange Ausführungszeit scheint aber tatsächlich am
sscanf zu liegen. Da wird wohl intern jedesmal noch ein strlen
aufgerufen, was bei deinem schier unendlich langen String natürlich ewig
dauert:

  https://stackoverflow.com/questions/23923924/why-is-glibcs-sscanf-vastly-slower-than-fscanf-on-linux

Ich frage mich allerdings, warum du die riesige Datei überhaupt komplett
einliest und nicht mit fscanf auswertest, wo das Problem nicht auftritt.

Edit: Das mit dem strlen hat ja rµ oben schon geschrieben. Sorry für die 
Wiederholung.

: Bearbeitet durch Moderator
Autor: Joachim D. (Firma: JDCC) (scheppertreiber)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Yalu X. schrieb:
> Der Grund für die lange Ausführungszeit scheint aber tatsächlich am
> sscanf zu liegen. Da wird wohl intern jedesmal noch ein strlen
> aufgerufen, was bei deinem schier unendlich langen String natürlich ewig
> dauert:

Genial. strlen ist doch bei X86 ein einziger Maschinenbefehl ?

Autor: Rolf M. (rmagnus)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Joachim D. schrieb:
> Genial. strlen ist doch bei X86 ein einziger Maschinenbefehl ?

Kann es sein. Das führt aber nicht dazu, dass das Lesen und Vergleichen 
von mehreren MB Speicher keine Zeit brauchen würde.

Autor: rµ (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Joachim D. schrieb:
> Genial. strlen ist doch bei X86 ein einziger Maschinenbefehl ?

Das ändert ja nix daran, dass es eine Schleife ist, die erst bei \0 
abbricht, und daher immer alle 3,5mb durchschauen muss. Ob das der 
Prozessor als Maschinenbefehl ausführt oder als Programm ist da 
unerheblich. Die Daten passen nicht in die schnellsten Caches des 
Prozessors, und schmeissen andere Daten eventuell sogar hinaus.

Das ganze passiert 2mal pro Schleifendurchlauf, also pro "Wort", was 
insgesamt eine quadratische Komplexität ergibt (Anzahl der 
Schleifendurchläufe ist proportional der Dateigröße behaupte ich mal 
salopp). In dem Fall also proportional zu 3,5e6^2, also proportional 
10^13 Operationen. Ist eigentlich ein Wunder, dass es doch so schnell 
ist ;-)

Autor: rµ (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
rµ schrieb:
> immer alle 3,5mb durchschauen

Natürlich in dem Fall nur von der aktuellen Position bis zum Ende des 
Puffers, der Puffer wird immer kleiner, das hilft aber nur einen Faktor 
2.

Autor: Joachim D. (Firma: JDCC) (scheppertreiber)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ich liege jetzt im Liveprogramm bei einer Ladezeit von 0.0789 sec.
Derber Unterschied zu 16 sec.

Ich danke allen die mir hier geholfen haben. Auf sscanf wäre
ich vermutlich nicht so schnell gekommen ;)

Autor: cppbert3 (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Joachim D. schrieb:
> Ladezeit von 0.0789 sec.

und auch noch schneller als 100ms - zum Glück misst clock() ~atomgenau~ 
:)

Autor: Joachim D. (Firma: JDCC) (scheppertreiber)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
cppbert3 schrieb:
> Joachim D. schrieb:
>> Ladezeit von 0.0789 sec.
>
> und auch noch schneller als 100ms - zum Glück misst clock() ~atomgenau~
> :)

Stimmt. Da muß ich mal das Isotop tauschen ;)

Autor: cppbert3 (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Joachim D. schrieb:
> cppbert3 schrieb:
>> Joachim D. schrieb:
>>> Ladezeit von 0.0789 sec.
>>
>> und auch noch schneller als 100ms - zum Glück misst clock() ~atomgenau~
>> :)
>
> Stimmt. Da muß ich mal das Isotop tauschen ;)

tausch lieber beide

Autor: Vlad T. (vlad_tepesch)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
rµ schrieb:
> 
https://stackoverflow.com/questions/23923924/why-is-glibcs-sscanf-vastly-slower-than-fscanf-on-linux
>
> Falls das MinGW das auch so macht passiert bei jedem sscanf ein strlen
> auf den string, das schaut also jeden Schleifendurchlauf den gesamten
> Puffer an.

krass - damit hätte ich auch nicht gerechnet.
das macht es doch bei fscanf/scanf auch nicht (prinzipbedingt) das man 
das da so einbaut...


Zumal hier scanf noch nichteinmal für das genutzt wurde, für das es da 
ist:
formtatierte Daten einparsen, sondern quasi immer nur zum Tokenizing.

Wofür es deutlich besser geeignete Funktionen gibt, wie ich schon hier 
meinte

Vlad T. schrieb:
> diese unsägliche  scword( p) / sscanf orgien.

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.