Laufzeiten und GNU/Watcom C

OP (Firma: JDCC) #6030635
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?
Moderator Persönliche Seite #6030672
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.
OP (Firma: JDCC) #6030742
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 :)
Moderator #6030759
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 ;-)
Gast #6030863
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 
;-)
OP (Firma: JDCC) #6031164
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 ...
Gast #6031185
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.
Gast #6031194
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.
Gast #6031683
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?
Gast #6031688
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?
OP (Firma: JDCC) #6034064
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 !
Angehängte Dateien:
Moderator Persönliche Seite #6034094
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.
OP (Firma: JDCC) #6034119
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
}
Gast #6034405
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?
OP (Firma: JDCC) #6034495
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.
Gast #6034554
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
Gast #6034579
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/
Gast #6034589
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
#6034613
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.
#6034649
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.
Gast #6034654
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
Gast #6034657
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
Gast #6034678
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?
#6034686
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.
Gast #6034688
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 :)
#6034705
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.
Gast #6034724
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
#6034849
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.
Gast #6034967
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?
Gast #6034993
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 ...
OP (Firma: JDCC) #6035311
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.
Gast #6035342
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).
Gast #6035348
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?
OP (Firma: JDCC) #6035360
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.
Gast #6035364
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.
#6035394
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 :-)
Gast #6035425
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).
Gast #6035431
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?
Gast #6035435
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
Gast #6035440
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
OP (Firma: JDCC) #6035450
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
Angehängte Dateien:
Gast #6035452
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.
#6035462
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
#6035483
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.
Gast #6035484
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.
Gast #6035486
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.
Gast #6035504
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.
#6035508
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
Gast #6035511
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.
Moderator #6035611
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.
Gast #6035635
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 ;-)
#6035906
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

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren