Forum: PC-Programmierung Laufzeiten und GNU/Watcom C


von Joachim D. (Firma: JDCC) (scheppertreiber)


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?

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


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:
1
#include <stdio.h>
2
3
int
4
main(int argc, char **argv)
5
{
6
  if (argc < 2)
7
    return 1;
8
9
  FILE *f;
10
  if ((f = fopen(argv[1], "r")) == NULL)
11
    return 2;
12
13
  char line[200];
14
  int lineno = 0;
15
16
  while (fgets(line, sizeof line, f)) {
17
    lineno++;
18
  }
19
  printf("Read %d lines\n", lineno);
20
21
  return 0;
22
}

und auf eins der Dictionaries loslasse, geht das in Millisekunden:
1
% time ./foo /usr/share/dict/swiss 
2
Read 356108 lines
3
0.010u 0.000s 0:00.01 100.0%    0+0k 0+0io 0pf+0w
4
% time ./foo /usr/share/dict/ngerman 
5
Read 356008 lines
6
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.

von Joachim D. (Firma: JDCC) (scheppertreiber)


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

von Yalu X. (yalu) (Moderator)


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

von (Gast)


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.

von Joachim D. (Firma: JDCC) (scheppertreiber)


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 ...

von (Gast)


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

von derMosphet (Gast)


Lesenswert?

Compiler Optimierungen (-O2) sind eingeschaltet?

von Joachim D. (Firma: JDCC) (scheppertreiber)


Lesenswert?

derMosphet schrieb:
> Compiler Optimierungen (-O2) sind eingeschaltet?

gibt 15,8 sec

von mh (Gast)


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.

von Joachim D. (Firma: JDCC) (scheppertreiber)


Lesenswert?

Klar ist da ein bug ;)

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

von mh (Gast)


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=...).

von Joachim D. (Firma: JDCC) (scheppertreiber)


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

von mh (Gast)


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?

von Joachim D. (Firma: JDCC) (scheppertreiber)


Lesenswert?

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

Ja.

von Joachim D. (Firma: JDCC) (scheppertreiber)


Lesenswert?

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

Es ändert sich nichts.

von (Gast)


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.

von Joachim D. (Firma: JDCC) (scheppertreiber)


Lesenswert?

Windows 10.

von Joachim D. (Firma: JDCC) (scheppertreiber)


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 ...

von (Gast)


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.

von Joachim D. (Firma: JDCC) (scheppertreiber)


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
von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

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

von (Gast)


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.

von Joachim D. (Firma: JDCC) (scheppertreiber)


Lesenswert?

Pause ;)

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

von Rolf M. (rmagnus)


Lesenswert?

Joachim D. schrieb:
> Windows 10.

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

von Joachim D. (Firma: JDCC) (scheppertreiber)


Lesenswert?

MinGW

von cppbert3 (Gast)


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?

von cppbert3 (Gast)


Lesenswert?


von cppbert3 (Gast)


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?

von Joachim D. (Firma: JDCC) (scheppertreiber)


Angehängte Dateien:

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 !

von mh (Gast)


Lesenswert?

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

Uns hilft der auch nicht ;-)

von Joachim D. (Firma: JDCC) (scheppertreiber)


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

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


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
von Joachim D. (Firma: JDCC) (scheppertreiber)


Lesenswert?

Puh. Das könnte mir gprof auch sagen. Ergo Sackgasse.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


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?

von Joachim D. (Firma: JDCC) (scheppertreiber)


Lesenswert?

1
        if ( ! LoadRightsD())
2
                return FALSE;
3
        jDspTime( NULL, "after load rights.d");
4
5
        // Kommentare, Tabs und NL durch Blanks ersetzen
6
        for ( p=fbuf; *p; p++){
7
                if ( *p == ';'){
8
                        for ( ; *p && *p != '\n'; p++){
9
                                *p = ' ';
10
                        }
11
                }
12
                if ( *p == '\n' ||  *p == '\r' ||  *p == '\t')
13
                        *p = ' ';
14
        }
15
        jDspTime( NULL, "after blank");
16
17
        for ( grpmax=usrmax=0, p=fbuf; *p; p++){
18
                *name = *workspace = 0;
19
                if ( memcmp( p, "group", 5) == 0){
20
                        p = scword( p); sscanf( p, "%s", name);
21
                        p = scword( p);
22
                        sscanf( p, "{%[^}]", workspace);
23
                        p += strlen( workspace) + 2;
24
                        GetGroup( name);
25
//                      jDspTime( NULL, "after get grp %s", name);
26
                }
27
                if ( memcmp( p, "user", 4) == 0){
28
                        p = scword( p); sscanf( p, "%s", name);
29
                        p = scword( p); sscanf( p, "{%[^}]", workspace);
30
                        p += strlen( workspace) + 2;
31
                        GetUser( name);
32
//                      jDspTime( NULL, "after get usr %s", name);
33
                }
34
        }
scword geht hinter den nächsten whitespace auf das nächste Wort.
In fbuf liegt die eingelesene Rechtdatei.
1
        static
2
        GetGroup(
3
        char    *name
4
        )
5
{
6
        char    *p, *q, *qq, job[256], parms[256];
7
        T_RJOB  *jp;
8
        int     i;
9
10
        strcpy( group[grpmax].id, name);
11
12
        // wenn in sys.cfg $timeout gesetzt ist dann als Vorgabe für
13
        // die Gruppe übernehmen
14
15
        if ( atoi( GetSysVar( "timeout"))){
16
                group[grpmax].timeout = atoi( GetSysVar( "timeout"));
17
//              JA( "timeout preset for grp '%s' set from sys.cfg: %d sec",
18
//                group[grpmax].id, group[grpmax].timeout);
19
        }
20
21
        p = workspace;
22
        if ( *p == ' ')
23
                p = scword( p);
24
25
        for ( ; *p; p = scword( p)){
26
                *job = *parms = 0;
27
                sscanf( p, "%s", job);
28
                if ( (q = strchr( job, '.')) != NULL){
29
                        *q = 0;
30
                        q++;
31
                } else {
32
                        q = job;
33
                }
34
                if ( strcmp( job, "isapo") == 0){
35
                        group[grpmax].isapo = TRUE;
36
                        continue;
37
                }
38
                if ( strcmp( job, "privat") == 0){
39
                        group[grpmax].privat = TRUE;
40
                        continue;
41
                }
42
                if ( strcmp( job, "testip") == 0){
43
                        group[grpmax].testip = TRUE;
44
                        continue;
45
                }
46
                if ( strcmp( job, "css") == 0){
47
                        p = scword( p);
48
                        sscanf( p, "(%[^)])", group[grpmax].css);
49
                        continue;
50
                }
51
                if ( strcmp( job, "timeout") == 0){
52
                        p = scword( p);
53
                        sscanf( p, "(%[^)])", parms);
54
                        group[grpmax].timeout = atoi( parms);
55
                        continue;
56
                }
57
58
                jp = NULL;
59
                for ( i=0; i<group[grpmax].jmax; i++){
60
                        if ( strcmp( group[grpmax].jobs[i].id, job) == 0){
61
                                jp = &group[grpmax].jobs[i];
62
                                break;
63
                        }
64
                }
65
                if ( jp == NULL){
66
                        jp = &group[grpmax].jobs[group[grpmax].jmax];
67
                        group[grpmax].jmax++;
68
                }
69
                strcpy( jp->id, job);
70
71
                p = scword( p);
72
                sscanf( p, "(%[^)])", parms);
73
74
                switch ( *q) {
75
                  case 'e':     // exec in docs ablegen
76
                  case 'd':
77
                        GetAccess( &jp->docs, parms);
78
                        break;
79
                  case 'n':
80
                        GetAccess( &jp->notes, parms);
81
                        break;
82
                  case 's':
83
                        if ( *(q+1) == 'c'){
84
                                strcpy( jp->scope, parms);
85
                                trim( jp->scope);
86
                        } else {
87
                                strcpy( jp->system, parms);
88
                        }
89
                        break;
90
                  case 'f':
91
                        StoreGrpFilter( jp, parms);
92
                        break;
93
                  default : ;
94
                }
95
                qq = p;
96
                if ( (p = strchr( p, ')')) == NULL){
97
                        JA( "*** missing ')' at group '%s' > '%s'", workspace, qq);
98
                        JSSayErr( "rights.d: missing ')' at group '%s' > '%s'", workspace, qq);
99
                        break;
100
                }
101
        }
102
        if ( ++grpmax >= GRPMAX){
103
                JSSayErr( "rights.d: Mehr als %d Gruppen definiert.", GRPMAX);
104
        }
105
        return 0;
106
}
1
        static
2
        GetUser(
3
        char    *name
4
        )
5
{
6
        char    *p, *q, *qq, tag[256], parms[256];
7
8
        strcpy( usr[usrmax].id, name);
9
        usr[usrmax].fmax = 0;
10
        p = workspace;
11
        if ( *p == ' ')
12
                p = scword( p);
13
14
        if ( atoi( GetSysVar( "timeout"))){
15
                usr[usrmax].timeout = atoi( GetSysVar( "timeout"));
16
//              JA( "timeout preset for usr '%s' set from sys.cfg: %d sec",
17
//                usr[usrmax].id, usr[usrmax].timeout);
18
        }
19
20
        for ( ; *p; p = scword( p)){
21
                *tag = *parms = 0;
22
                sscanf( p, "%s", tag);
23
                if ( strcmp( tag, "isapo") == 0){
24
                        usr[usrmax].isapo = TRUE;
25
                        continue;
26
                }
27
                if ( strcmp( tag, "privat") == 0){
28
                        usr[usrmax].privat = TRUE;
29
                        continue;
30
                }
31
                if ( strcmp( tag, "testip") == 0){
32
                        usr[usrmax].testip = TRUE;
33
                        continue;
34
                }
35
                p = scword( p);
36
                sscanf( p, "(%[^)])", parms);
37
                p += strlen( parms);
38
                trim( parms);
39
                if ( (p = strchr( p, ')')) == NULL){
40
                        JA( "*** missing ')' at '%s'", usr[usrmax].id);
41
                        JSSayErr( "rights.d: missing ')' at user '%s'", usr[usrmax].id);
42
                        break;
43
                }
44
45
                switch ( *tag) {
46
                  case 't': usr[usrmax].timeout = atoi( parms); break;
47
                  case 'm': strcpy( usr[usrmax].mail , parms); break;
48
                  case 'c': strcpy( usr[usrmax].css  , parms); break;
49
                  case 'a': strcpy( usr[usrmax].abt  , parms); break;
50
                  case 'n': strcpy( usr[usrmax].name , parms); break;
51
                  case 'p': strcpy( usr[usrmax].pass , parms); break;
52
                  case 'e': strcpy( usr[usrmax].expd , parms); break;
53
                  case 'g':
54
                        strcpy( usr[usrmax].group, parms);
55
                        break;
56
                  case 'f':
57
                        sscanf( parms, "%s%s%s",
58
                          usr[usrmax].filter[usr[usrmax].fmax].id,
59
                          usr[usrmax].filter[usr[usrmax].fmax].field,
60
                          usr[usrmax].filter[usr[usrmax].fmax].op);
61
62
                        q = scword( scword( scword( parms)));
63
                        if ( (qq = strstr( q, "hide")) != NULL){
64
                                memset( qq, ' ', 4);
65
                                usr[usrmax].filter[usr[usrmax].fmax].hide = TRUE;
66
                        }
67
                        trim( q);
68
69
                        strcpy( usr[usrmax].filter[usr[usrmax].fmax].expr, q);
70
                        usr[usrmax].fmax++;
71
                        break;
72
                  default : JA( "*** invalid entry %s\nworkspace: '%s'", tag, workspace);
73
                }
74
        }
75
        usrmax++;
76
        return 0;
77
}

: Bearbeitet durch Moderator
von mh (Gast)


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.

von Joachim D. (Firma: JDCC) (scheppertreiber)


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.

von Frank M. (ukw) (Moderator) Benutzerseite


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.

von leo (Gast)


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

von Joachim D. (Firma: JDCC) (scheppertreiber)


Lesenswert?

Virenscanner ist nicht drauf.
gprof ist 2.28

von mh (Gast)


Lesenswert?

Joachim D. schrieb:
> Wenn ich da etwas rauswerfe wird es unklar.

Unklarer als jetzt wirds nicht ;-)

von cppbert3 (Gast)


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?

von Joachim D. (Firma: JDCC) (scheppertreiber)


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.

von cppbert3 (Gast)


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

von Joachim D. (Firma: JDCC) (scheppertreiber)


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.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Was passiert, wenn du mal
1
#define GetSysVar(foo) "42"

davor setzt?

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

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

von cppbert3 (Gast)


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/

von cppbert3 (Gast)


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

von Vlad T. (vlad_tepesch)


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:
1
   char *token;
2
   const char s[] = " ";
3
4
   /* get the first token */
5
   token = strtok(str, s);
6
   
7
   /* walk through other tokens */
8
   while( token != NULL ) {
9
      printf( " '%s'\n", token );
10
    
11
      token = strtok(NULL, s);
12
   }



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
von Joachim D. (Firma: JDCC) (scheppertreiber)


Lesenswert?

cppbert3 schrieb:
> also 14371 x 2 = 28742 Aufrufe von GetSysVar+atoi

ausgelagert. Immer noch 16,2 sec.

von cppbert3 (Gast)


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

von Joachim D. (Firma: JDCC) (scheppertreiber)


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 ?

von Vlad T. (vlad_tepesch)


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
von cppbert3 (Gast)


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

von cppbert3 (Gast)


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

von Joachim D. (Firma: JDCC) (scheppertreiber)


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 ...

von Joachim D. (Firma: JDCC) (scheppertreiber)


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.

von Joachim D. (Firma: JDCC) (scheppertreiber)


Lesenswert?

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

von mh (Gast)


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.

von cppbert3 (Gast)


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?

von Joachim D. (Firma: JDCC) (scheppertreiber)


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

von Vlad T. (vlad_tepesch)


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.

von cppbert3 (Gast)


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

von Vlad T. (vlad_tepesch)


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.

von cppbert3 (Gast)


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

von mh (Gast)


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?

von cppbert3 (Gast)


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?

von Vlad T. (vlad_tepesch)


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):
1
static
2
GetUser(
3
         char    *name
4
        )
5
{
?

Hast du dir mal deine Compilerwarnungen angeschaut?

Ich würde die So definieren:
1
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
von Joachim D. (Firma: JDCC) (scheppertreiber)


Lesenswert?

Ich teste das morgen erstmal in Ruhe durch.

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

von foobar (Gast)


Lesenswert?

Btw, möglicher read-past-end-of-buffer (fbuf=";") in
1
        // Kommentare, Tabs und NL durch Blanks ersetzen
2
        for ( p=fbuf; *p; p++){
3
                if ( *p == ';'){
4
                        for ( ; *p && *p != '\n'; p++){
5
                                *p = ' ';
6
                        }
7
                }
8
                if ( *p == '\n' ||  *p == '\r' ||  *p == '\t')
9
                        *p = ' ';
10
        }

Wie sieht scword aus?

von Joachim D. (Firma: JDCC) (scheppertreiber)


Lesenswert?

1
char *
2
        scword(
3
        char     *P
4
        )
5
{
6
        unsigned char *p;
7
8
        p = (unsigned char*) P;
9
        if ( isspace( *p)){
10
                for ( ; isspace( *p); p++) ;
11
        } else {
12
                for ( ; *p  && ! isspace( *p); p++) ;
13
                for ( ; isspace( *p); p++) ;
14
        }
15
        return p;
16
}

: Bearbeitet durch Moderator
von foobar (Gast)


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 ...

von Joachim D. (Firma: JDCC) (scheppertreiber)


Lesenswert?

1
        for ( grpmax=usrmax=0, p=fbuf; *p; p++){
2
                *name = *workspace = 0;
3
                if ( memcmp( p, "user", 4) == 0){
4
                        p = scword( p); // sscanf( p, "%s", name);
5
                        p = scword( p); sscanf( p, "{%[^}]", workspace);
6
                        p += strlen( workspace) + 2;
7
                }
8
        }
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.

von cppbert3 (Gast)


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

von Joachim D. (Firma: JDCC) (scheppertreiber)


Lesenswert?

Liegt irgendwie auf der Hand ... ;(

von Joachim D. (Firma: JDCC) (scheppertreiber)


Lesenswert?

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

von cppbert3 (Gast)


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

von mh (Gast)


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?

von Joachim D. (Firma: JDCC) (scheppertreiber)


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.

von mh (Gast)


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.

von Eric B. (beric)


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:
1
char *scword(char *P) {
2
  char *p = P;
3
  
4
  // skip non-space
5
  while (*p && !isspace(*p)) {
6
    p++;
7
  }
8
9
  // skip space 
10
  while (*p && isspace(*p))  {
11
    p++;
12
  }
13
14
  return p;
15
}
Macht das gleiche, aber mMn übersichtlicher und ohne Überlauf :-)

von Rolf M. (rmagnus)


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
von Joachim D. (Firma: JDCC) (scheppertreiber)


Angehängte Dateien:

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
von foobar (Gast)


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

von cppbert3 (Gast)


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?

von (Gast)


Lesenswert?

Die Spannung steigt. Testdaten würd man noch brauchen.

von cppbert3 (Gast)


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

von cppbert3 (Gast)


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

von Yalu X. (yalu) (Moderator)


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.

von Joachim D. (Firma: JDCC) (scheppertreiber)


Angehängte Dateien:

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

von foobar (Gast)


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.

von Joachim D. (Firma: JDCC) (scheppertreiber)


Lesenswert?

Danke foobar,

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

Irgendwie wird das richtig spannend ...

von Eric B. (beric)


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

von Joachim D. (Firma: JDCC) (scheppertreiber)


Lesenswert?

isspace( *p) durch *p == ' ' ersetzt, keine Änderung

von cppbert3 (Gast)


Lesenswert?

Joachim D. schrieb:
> Die sollte ich erst anonymisieren. Dauert ein wenig ...

das wäre perfekt

von cppbert3 (Gast)


Lesenswert?

Joachim D. schrieb:
> Ohne #pragma gleiche Laufzeit.

hat in deinem Beispiel ja auch keine Auswirkung

von (Gast)


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.

von Eric B. (beric)


Lesenswert?

Eigentlich kann man das eindampfen auf:
1
char *scword(unsigned char *p) {
2
  for ( ; *p && !isspace(*p); p++) ;
3
  for ( ; *p &&  isspace(*p); p++) ;
4
  return p;
5
}

von Rolf M. (rmagnus)


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.

von (Gast)


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.

von foobar (Gast)


Lesenswert?

Mit so nen Scheiß mußte sich Watcom noch nicht rumschlagen:
1
# echo $LANG
2
en_US.UTF-8
3
# echo ä | grep '[ab]'
4
# echo ä | grep '[a-b]'
5
ä
6
# echo ä | LANG=C grep '[a-b]'
7
#

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.

von (prx) A. K. (prx)


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.

von foobar (Gast)


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!

von Kaj (Gast)


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:
1
[/home/ghost/devel/c_test/main.c:63] (warning) sscanf() without field width 
2
limits can crash with huge input data. Add a field width specifier to fix 
3
this problem.
4
5
Sample program that can crash:
6
7
#include <stdio.h>
8
int main()
9
{
10
    char c[5];
11
    scanf("%s", c);
12
    return 0;
13
}
14
15
Typing in 5 or more characters may make the program crash. The correct usage 
16
here is 'scanf("%4s", c);', as the maximum field width does not include the 
17
terminating null byte.
18
Source: http://linux.die.net/man/3/scanf
19
Source: http://www.opensource.apple.com/source/xnu/xnu-1456.1.26/libkern/stdio/scanf.c [invalidscanf]
20
21
[/home/ghost/devel/c_test/main.c:64] (warning) sscanf() without field width 
22
limits can crash with huge input data. Add a field width specifier to fix 
23
this problem.

von cppbert3 (Gast)


Lesenswert?

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

viel schöner geht nicht :)

von Rolf M. (rmagnus)


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

von (Gast)


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.

von Joachim D. (Firma: JDCC) (scheppertreiber)


Angehängte Dateien:

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.

von Joachim D. (Firma: JDCC) (scheppertreiber)


Angehängte Dateien:

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ß).

von cppbert3 (Gast)


Lesenswert?

dann bleibt dir nur die scanfs aus zu tauschen

von Yalu X. (yalu) (Moderator)


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
von Joachim D. (Firma: JDCC) (scheppertreiber)


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 ?

von Rolf M. (rmagnus)


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.

von (Gast)


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

von (Gast)


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.

von Joachim D. (Firma: JDCC) (scheppertreiber)


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

von cppbert3 (Gast)


Lesenswert?

Joachim D. schrieb:
> Ladezeit von 0.0789 sec.

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

von Joachim D. (Firma: JDCC) (scheppertreiber)


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

von cppbert3 (Gast)


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

von Vlad T. (vlad_tepesch)


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.

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.