Autor |
Beitrag |
Gabriel
Hält's aus hier
Beiträge: 9
XP Home (Op9)
Delphi 7, PHP
|
Verfasst: Di 04.09.07 22:56
Hallo,
ich wollte ein Programm zum analysieren meiner Apache-Logs schreiben, insbesondere, um IPs zu filtern, da ich zur Zeit einige Probleme mit SPAM in einem Gästebuch habe.
Nun habe ich ein Delphi-Programm geschrieben, dass diese Aufgabe erledigen soll, bei kleineren Datensätzen geht das auch ohne Probleme. Nun habe ich allerdings einen kompletten Wochendatensatz (10MB) mit dem Programm eingelesen, das sind ca. 50000 Zeilen, die in ein 8-dimelsionales Array eingelesen werden. Nach ein paar Minuten mit 100% CPU-Last und 500 MB belegtem RAM musste ich das Programm abschießen...
Ist die Datenmange einfach zu groß, oder lässt sich durch Optimierung des Quellcodes ein deutlich geringerer Ressourcenbedarf realisieren, sodass ich mit meinem Rechner (3,4 Ghz, 1 GB RAM) ohne größere Verzögerungen (> 15 sec) an die 10000 Zeilen verabeiten kann?
Gruß,
Gabriel
P.S.: Ich bin Delphi-Neuling und habe erst wenige kleine Delphi-Progrämmchen geschrieben.
|
|
jaenicke
      
Beiträge: 19315
Erhaltene Danke: 1747
W11 x64 (Chrome, Edge)
Delphi 11 Pro, Oxygene, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
|
Verfasst: Di 04.09.07 23:09
Kann es sein, dass du zum Beispiel das Array bei jeder einzelnen Zeile mit SetLength vergrößerst? Ich denke man kann das auf jeden Fall so optimieren, dass die Laufzeit akzeptabel ist.
Dafür müsstest du dann aber mal den Quelltext posten, mit dem du das machst, damit ich dazu mehr sagen kann. Ich werde mal ein kleines Beispiel schreiben und nachsehen, wie lange mein Quelltext für 10000 Zeilen braucht.
|
|
dummzeuch
      
Beiträge: 593
Erhaltene Danke: 5
Delphi 5 ent, Delphi 6 bis Delphi XE8 pro
|
Verfasst: Di 04.09.07 23:13
Hi,
Gabriel hat folgendes geschrieben: | Nun habe ich ein Delphi-Programm geschrieben, dass diese Aufgabe erledigen soll, bei kleineren Datensätzen geht das auch ohne Probleme. Nun habe ich allerdings einen kompletten Wochendatensatz (10MB) mit dem Programm eingelesen, das sind ca. 50000 Zeilen, die in ein 8-dimelsionales Array eingelesen werden. Nach ein paar Minuten mit 100% CPU-Last und 500 MB belegtem RAM musste ich das Programm abschießen...
Ist die Datenmange einfach zu groß, oder lässt sich durch Optimierung des Quellcodes ein deutlich geringerer Ressourcenbedarf realisieren, sodass ich mit meinem Rechner (3,4 Ghz, 1 GB RAM) ohne größere Verzögerungen (> 15 sec) an die 10000 Zeilen verabeiten kann? |
Vermutlich benutzt Du viele Stringmanipulationen, die am Ende zu Speicherfragmentierung fuehren. Das geht sowohl auf die Performance als auch auf den Speicherverbrauch. Das laesst sich in der Regel durch einfache Aenderungen vermeiden. Zeig mal was Code.
twm
|
|
Gabriel 
Hält's aus hier
Beiträge: 9
XP Home (Op9)
Delphi 7, PHP
|
Verfasst: Di 04.09.07 23:22
jaenicke hat folgendes geschrieben: | Kann es sein, dass du zum Beispiel das Array bei jeder einzelnen Zeile mit SetLength vergrößerst? Ich denke man kann das auf jeden Fall so optimieren, dass die Laufzeit akzeptabel ist. |
Vielen Dank für den Tipp!
Ja, ich vergrößer(t)e das Array tatsächlich mit SetLength. Nachdem ich die Größe nun schon am Anfang definiert habe, braucht das einlesen von 50000 Zeilen nur noch ungefähr eine Sekunde.
So funktioniert das schon VIEL schneller. Was wäre nun aber besser, wenn ich mit einer Anzahl zwischen 50000 und 250000 Elementen arbeiten will - das Array von Anfang an auf 250000 Elemente setzen oder auf z.B. 50000 und kurz vor jeder Überschreitung um 50000 erhöhen?
|
|
Gabriel 
Hält's aus hier
Beiträge: 9
XP Home (Op9)
Delphi 7, PHP
|
Verfasst: Di 04.09.07 23:28
dummzeuch hat folgendes geschrieben: | Hi,
Gabriel hat folgendes geschrieben: | Nun habe ich ein Delphi-Programm geschrieben, dass diese Aufgabe erledigen soll, bei kleineren Datensätzen geht das auch ohne Probleme. Nun habe ich allerdings einen kompletten Wochendatensatz (10MB) mit dem Programm eingelesen, das sind ca. 50000 Zeilen, die in ein 8-dimelsionales Array eingelesen werden. Nach ein paar Minuten mit 100% CPU-Last und 500 MB belegtem RAM musste ich das Programm abschießen...
Ist die Datenmange einfach zu groß, oder lässt sich durch Optimierung des Quellcodes ein deutlich geringerer Ressourcenbedarf realisieren, sodass ich mit meinem Rechner (3,4 Ghz, 1 GB RAM) ohne größere Verzögerungen (> 15 sec) an die 10000 Zeilen verabeiten kann? |
Vermutlich benutzt Du viele Stringmanipulationen, die am Ende zu Speicherfragmentierung fuehren. Das geht sowohl auf die Performance als auch auf den Speicherverbrauch. Das laesst sich in der Regel durch einfache Aenderungen vermeiden. Zeig mal was Code.
twm |
Ich habe das Einlesen in der Art gemacht:
Quelltext 1: 2: 3: 4:
| zeile := copy(zeile, pos2 + 2, length(zeile) - (pos2 + 1)); pos2 := pos('"', copy(zeile, 2, length(zeile))) + 1; temp_element := copy(zeile, pos1, pos2 - pos1); log[array_length][6] := temp_element; |
Und das in etwas variierter Form acht mal hintereinander, da die Trennzeichen nicht immer gleich sind.
|
|
jaenicke
      
Beiträge: 19315
Erhaltene Danke: 1747
W11 x64 (Chrome, Edge)
Delphi 11 Pro, Oxygene, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
|
Verfasst: Di 04.09.07 23:35
Du musst die Größe nicht unbedingt am Anfang definieren, du solltest mit einem Delta-Wert arbeiten. Das heißt wenn kein Eintrag mehr frei ist, dann vergrößere um den Delta-Wert, zum Beispiel um 1000. So vergrößerst du nur noch bei jedem tausendsten Mal. Und am Ende verkleinerst du um die Anzahl der freien Plätze.
|
|
Gabriel 
Hält's aus hier
Beiträge: 9
XP Home (Op9)
Delphi 7, PHP
|
Verfasst: Di 04.09.07 23:38
jaenicke hat folgendes geschrieben: | Du musst die Größe nicht unbedingt am Anfang definieren, du solltest mit einem Delta-Wert arbeiten. Das heißt wenn kein Eintrag mehr frei ist, dann vergrößere um den Delta-Wert, zum Beispiel um 1000. So vergrößerst du nur noch bei jedem tausendsten Mal. Und am Ende verkleinerst du um die Anzahl der freien Plätze. |
Ok, so werde ich es dann machen - gibt es eigentlich irgendein Limit, z.B. dass ein Array nur 1.000.000 Elemente umfassen darf oder so? Sonst könnte man ja beinahe ein ganzes Jahr auf einmal filtern...
Sry, falls die Frage vielleicht etwas blöd ist, ich programmiere nicht viel in Delphi, dafür sehr viel in PHP, wo man ja meist die 8 MB - Grenze beachten muss.
|
|
dummzeuch
      
Beiträge: 593
Erhaltene Danke: 5
Delphi 5 ent, Delphi 6 bis Delphi XE8 pro
|
Verfasst: Mi 05.09.07 00:17
Gabriel hat folgendes geschrieben: | dummzeuch hat folgendes geschrieben: | Vermutlich benutzt Du viele Stringmanipulationen, die am Ende zu Speicherfragmentierung fuehren. Das geht sowohl auf die Performance als auch auf den Speicherverbrauch. Das laesst sich in der Regel durch einfache Aenderungen vermeiden. Zeig mal was Code. |
Ich habe das Einlesen in der Art gemacht:
Quelltext 1: 2: 3: 4:
| zeile := copy(zeile, pos2 + 2, length(zeile) - (pos2 + 1)); pos2 := pos('"', copy(zeile, 2, length(zeile))) + 1; temp_element := copy(zeile, pos1, pos2 - pos1); log[array_length][6] := temp_element; |
Und das in etwas variierter Form acht mal hintereinander, da die Trennzeichen nicht immer gleich sind. |
Das sieht eigenlich unkritisch aus. Aber jaenicke scheint ja Recht zu haben mit seiner Vermutung, dass es an der dynamischen Vergroesserung des Arrays liegt. Daran hatte ich noch gar nicht gedacht.
Maximale Groesse eines Arrays in Delphi ist uebrigens 2 GB. Wenn es ein String-Array ist, braucht jeder Eintrag nur 4 Bytes, es passen also max 2 gb / 4 / dim2 Eintrage rein. Allerdings kann ein Programm insgesamt nur 2 GB Speicher verwenden (oder sind das inzwischen 4?), d.h. es gehen auch noch die Inhalte der Strings + 8 Bytes String-Deskriptoren sowie alle anderen Variablen und der Overhead des Speichermanagers davon ab. Aber soweit wirst Du vermutlich nicht kommen, denn sobald die Kiste anfaengt zu swappen geht die Performance sowas von in den Keller...
Dann muesste man wieder auf die alten Tricks zum Speicher sparen zurueckgreifen, die man schon zu DOS-Zeiten eingesetzt hat. :-| Ich schaetze aber mal, dass Du das gar nicht brauchst.
twm
|
|
BenBE
      
Beiträge: 8721
Erhaltene Danke: 191
Win95, Win98SE, Win2K, WinXP
D1S, D3S, D4S, D5E, D6E, D7E, D9PE, D10E, D12P, DXEP, L0.9\FPC2.0
|
Verfasst: Mi 05.09.07 00:18
Es gibt eigentlich nur zwei Grenzen:
Der virtuelle Adressraum und die Dauer eines Menschenlebens. Im Regelfall ist ersteres das Problem
Im Normalfall nimmt man aber bei größeren Datenmengen zwei Schritte: 1. Daten importieren (in eine DB) und 2. Filtern der Datenmenge.
@Spam-Problem: Hol Dir von MaxMind die GeoIP-Datenbank. Ich glaub, die könnt Dir da schon helfen 
_________________ Anyone who is capable of being elected president should on no account be allowed to do the job.
Ich code EdgeMonkey - In dubio pro Setting.
|
|
Gabriel 
Hält's aus hier
Beiträge: 9
XP Home (Op9)
Delphi 7, PHP
|
Verfasst: Mi 05.09.07 10:45
Danke euch beiden für die ganzen Antworten.
Ok, dann scheint's wohl so, als ob ich auch etwas größere Datein verwenden könnte...
Der Zeitpunkt, an dem der Rechner anfängt mit swappen ist nautürlich ein kritischer Zeitpunkt, aber dank Raid 0 nicht die absolute Deadline  .
Ich hoff, dass ich keine DB's brauche - die verwende ich nicht sonderlich gern (obwohl ich weiß, dass sie unter Umständen viele Vorteile bringen). In PHP habe ich auch ein Content Management System ohne MySQL geschrieben (Daten einfach in "Textdateien" rein)  .
Wegen Spam: Die Spammer/Bots haben zum Spam-Zeitpunkt immer nur auf das Sicherheitsbild und die PHP-Datei, die die Einträge erstellt, zugegriffen. Nun wollte ich unter anderem erstmal schauen, wann die IPs alle die Page gescannt haben bzw. ob vielleicht nur einmal gescannt wurde und es nur ein Spammer ist, der über unterscheidliche Proxys in unterscheidlicehn Netzwerken mit unterschiedlichen Browser- und Systemkennungen "arbeitet".
|
|
|