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?
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 | |
2 | |
3 | |
4 | |
5 | |
6 | |
7 | |
8 | |
9 | |
10 | |
11 | |
12 | |
13 | |
14 | |
15 | |
16 | |
17 | |
18 | |
19 | |
20 | |
21 | |
22 | |
und auf eins der Dictionaries loslasse, geht das in Millisekunden:
1 | |
2 | |
3 | |
4 | |
5 | |
6 | |
Beide Dateien sind 4,5 MiB groß, also in dieser Hinsicht ungefähr vergleichbar mit deiner.
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 :)
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
#6030770
GNU gprof sollte weiterhelfen, damit wird man recht schnell eingrenzen können was die 17s an Zeit kostet. Vermutlich irgendein unbeabsichtigter Systemaufruf pro Zeichen.
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 ...
Gast
#6030863
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 ;-)
Gast
#6030880
Compiler Optimierungen (-O2) sind eingeschaltet?
derMosphet schrieb: > Compiler Optimierungen (-O2) sind eingeschaltet? gibt 15,8 sec
Gast
#6030908
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.
Klar ist da ein bug ;) time( NULL) gibt auf einmal 0 ??? Gerade gefunden.
Gast
#6030965
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=...).
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 ;)
Gast
#6031007
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?
mh schrieb: > Du änderst mit einer Compileroption das Alignement für alle Strukturen? Ja.
Das ganze SSL-Zeuch mal totgelegt und #pragma pack(1) ganz oben ins erste Headerfile. Es ändert sich nichts.
Gast
#6031130
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.
Windows 10.
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
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.
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.
Kannst du denn nicht mal ein Stück Beispielcode zitieren, ohne nun gleich allerlei Firmengeheimnisse preiszugeben?
Gast
#6031194
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.
Pause ;) Gleich kommt Besuch und ich muß noch die Schweinshaxn holen. Am Sonntag geht weiter ;)))
Joachim D. schrieb: > Windows 10. Was für ein gcc ist das? mingw? cygwin? wsl?
MinGW
Gast
#6031683
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
#6031684
Gast
#6031688
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?
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 !
Gast
#6034073
Joachim D. schrieb: > Den etwas merkwürdigen output (der mir nicht weiterhilft, alles > 0) hängt an. Uns hilft der auch nicht ;-)
Edit: beim lauf von gprof kamen jede Menge Meldungen "BFD: Dwarf Error: Could not find abbrev number 84." Was Zwerge damit zu tun haben .... ;)
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.
Puh. Das könnte mir gprof auch sagen. Ergo Sackgasse.
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?
1 | |
2 | |
3 | |
4 | |
5 | |
6 | |
7 | |
8 | |
9 | |
10 | |
11 | |
12 | |
13 | |
14 | |
15 | |
16 | |
17 | |
18 | |
19 | |
20 | |
21 | |
22 | |
23 | |
24 | |
25 | |
26 | |
27 | |
28 | |
29 | |
30 | |
31 | |
32 | |
33 | |
34 | |
scword geht hinter den nächsten whitespace auf das nächste Wort. In fbuf liegt die eingelesene Rechtdatei.
1 | |
2 | |
3 | |
4 | |
5 | |
6 | |
7 | |
8 | |
9 | |
10 | |
11 | |
12 | |
13 | |
14 | |
15 | |
16 | |
17 | |
18 | |
19 | |
20 | |
21 | |
22 | |
23 | |
24 | |
25 | |
26 | |
27 | |
28 | |
29 | |
30 | |
31 | |
32 | |
33 | |
34 | |
35 | |
36 | |
37 | |
38 | |
39 | |
40 | |
41 | |
42 | |
43 | |
44 | |
45 | |
46 | |
47 | |
48 | |
49 | |
50 | |
51 | |
52 | |
53 | |
54 | |
55 | |
56 | |
57 | |
58 | |
59 | |
60 | |
61 | |
62 | |
63 | |
64 | |
65 | |
66 | |
67 | |
68 | |
69 | |
70 | |
71 | |
72 | |
73 | |
74 | |
75 | |
76 | |
77 | |
78 | |
79 | |
80 | |
81 | |
82 | |
83 | |
84 | |
85 | |
86 | |
87 | |
88 | |
89 | |
90 | |
91 | |
92 | |
93 | |
94 | |
95 | |
96 | |
97 | |
98 | |
99 | |
100 | |
101 | |
102 | |
103 | |
104 | |
105 | |
106 | |
1 | |
2 | |
3 | |
4 | |
5 | |
6 | |
7 | |
8 | |
9 | |
10 | |
11 | |
12 | |
13 | |
14 | |
15 | |
16 | |
17 | |
18 | |
19 | |
20 | |
21 | |
22 | |
23 | |
24 | |
25 | |
26 | |
27 | |
28 | |
29 | |
30 | |
31 | |
32 | |
33 | |
34 | |
35 | |
36 | |
37 | |
38 | |
39 | |
40 | |
41 | |
42 | |
43 | |
44 | |
45 | |
46 | |
47 | |
48 | |
49 | |
50 | |
51 | |
52 | |
53 | |
54 | |
55 | |
56 | |
57 | |
58 | |
59 | |
60 | |
61 | |
62 | |
63 | |
64 | |
65 | |
66 | |
67 | |
68 | |
69 | |
70 | |
71 | |
72 | |
73 | |
74 | |
75 | |
76 | |
77 | |
Gast
#6034134
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.
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.
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.
Gast
#6034153
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
Virenscanner ist nicht drauf. gprof ist 2.28
Gast
#6034161
Joachim D. schrieb: > Wenn ich da etwas rauswerfe wird es unklar. Unklarer als jetzt wirds nicht ;-)
Gast
#6034405
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?
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
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
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.
Was passiert, wenn du mal
1 | |
davor setzt? Dieses sich wiederholende
1 | |
stößt mir irgendwie auf. Solch eine String->Int-Konvertierung macht man doch am besten nur genau einmal beim Systemstart.
Gast
#6034579
>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
@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
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 | |
2 | |
3 | |
4 | |
5 | |
6 | |
7 | |
8 | |
9 | |
10 | |
11 | |
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.
cppbert3 schrieb: > also 14371 x 2 = 28742 Aufrufe von GetSysVar+atoi ausgelagert. Immer noch 16,2 sec.
Gast
#6034619
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
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 ?
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
>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
>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
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 ...
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.
strtok zerstört mir den Quellstring. Gerade beim Einlesen von Wertepaaren nicht so toll.
Gast
#6034672
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.
Gast
#6034678
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?
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 ;)
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
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 :)
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
>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
Gast
#6034728
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?
Gast
#6034739
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?
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 | |
2 | |
3 | |
4 | |
5 | |
? Hast du dir mal deine Compilerwarnungen angeschaut? Ich würde die So definieren:
1 | |
(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.
Ich teste das morgen erstmal in Ruhe durch. Irgendwo muß aber ein Unterschied beider Compiler sein. Den suche ich.
Gast
#6034967
Btw, möglicher read-past-end-of-buffer (fbuf=";") in
1 | |
2 | |
3 | |
4 | |
5 | |
6 | |
7 | |
8 | |
9 | |
10 | |
Wie sieht scword aus?
1 | |
2 | |
3 | |
4 | |
5 | |
6 | |
7 | |
8 | |
9 | |
10 | |
11 | |
12 | |
13 | |
14 | |
15 | |
16 | |
Gast
#6034993
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 ...
1 | |
2 | |
3 | |
4 | |
5 | |
6 | |
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
#6035315
>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
Liegt irgendwie auf der Hand ... ;(
Mein erster C-Compiler Manx Aztek konnte nur das 'C'-locale, der Watcom (glaube ich) auch. Beim GCC ist das anscheinend etwas komplexer.
Gast
#6035342
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
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?
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
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.
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 | |
2 | |
3 | |
4 | |
5 | |
6 | |
7 | |
8 | |
9 | |
10 | |
11 | |
12 | |
13 | |
14 | |
15 | |
Macht das gleiche, aber mMn übersichtlicher und ohne Überlauf :-)
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.
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
Gast
#6035425
> 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
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
#6035433
Die Spannung steigt. Testdaten würd man noch brauchen.
Gast
#6035435
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
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
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.
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
Gast
#6035452
> 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.
Danke foobar, ich schaue mir das nachher nochmal genauer an. Ich muß jetzt mal 2 h weg ;(. Irgendwie wird das richtig spannend ...
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
isspace( *p) durch *p == ' ' ersetzt, keine Änderung
Gast
#6035464
Joachim D. schrieb: > Die sollte ich erst anonymisieren. Dauert ein wenig ... das wäre perfekt
Gast
#6035474
Joachim D. schrieb: > Ohne #pragma gleiche Laufzeit. hat in deinem Beispiel ja auch keine Auswirkung
Gast
#6035476
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.
Eigentlich kann man das eindampfen auf:
1 | |
2 | |
3 | |
4 | |
5 | |
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
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
Mit so nen Scheiß mußte sich Watcom noch nicht rumschlagen:
1 | |
2 | |
3 | |
4 | |
5 | |
6 | |
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.
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.
Gast
#6035498
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!
Gast
#6035504
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 | |
2 | |
3 | |
4 | |
5 | |
6 | |
7 | |
8 | |
9 | |
10 | |
11 | |
12 | |
13 | |
14 | |
15 | |
16 | |
17 | |
18 | |
19 | |
20 | |
21 | |
22 | |
23 | |
Gast
#6035507
>char *scword(unsigned char *p) { > for ( ; *p && !isspace(*p); p++) ; > for ( ; *p && isspace(*p); p++) ; > return p; >} viel schöner geht nicht :)
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
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.
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.
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ß).
Gast
#6035610
dann bleibt dir nur die scanfs aus zu tauschen
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.
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 ?
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.
Gast
#6035635
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 ;-)
Gast
#6035641
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.
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 ;)
Gast
#6035822
Joachim D. schrieb: > Ladezeit von 0.0789 sec. und auch noch schneller als 100ms - zum Glück misst clock() ~atomgenau~ :)
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 ;)
Gast
#6035844
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
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.