Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Die Delphi-IDE (https://www.delphipraxis.net/62-die-delphi-ide/)
-   -   Benutzerdefinierte Compiler-Warnungen (https://www.delphipraxis.net/186056-benutzerdefinierte-compiler-warnungen.html)

Scurra 30. Jul 2015 10:50

Benutzerdefinierte Compiler-Warnungen
 
Hallo zusammen,

ich habe vor kurzem die Performance einer Software überprüft und mir ist aufgefallen, dass es eine Stelle gibt, an der die Performance sehr schlecht ist: Man hat dort eine Methode NodeByAttributeValue eines Xml-Knotens, die Sub-Knoten mit einem bestimmten Namen, Attributnamen und Attributwert sucht. Es gibt zusätzlich einen Methodenparameter "Should-Recurse: Boolean = True", der angibt, ob die Methode auch rekursiv die Sub-Knoten selbst durchsuchen soll.

Das Problem dabei ist, dass wir das in der Regel nicht möchten, da wir meistens schon direkt zum Vater-Knoten navigieren.

In unserer Software wird der Parameter in der Regel nicht explizit angegeben, da das bislang niemandem aufgefallen ist. Jetzt möchten wir die Performance verbessern. Da diese Methode aus einer Fremdkomponente stammt, möchten wir nicht einfach den Default-Wert des Parameters auf False setzen. Es ist auch kein Problem, an allen Stellen, an denen wir die Methode aufrufen, nachträglich explizit ein false zu übergeben.


Meine Frage ist nun, ob es möglich ist, in diesem speziellen Fall eine Compiler-Meldung ausgeben zu lassen, wenn man den Parameter ShouldRecurse beim Methodenaufruf nicht explizit angegeben hat, damit man den Fehler später nicht noch einmal macht oder zumindest auf den Fehler hingewiesen wird ;) Ich konnte bisher leider nichts finden und ich glaube auch eher nicht, dass es möglich ist, denn wenn man den Default-Parameter explizit angeben möchte, warum entfernt man dann nicht einfach den Default-Wert? In unserem Fall ist der Grund (wie oben schon erwähnt) dass es sich um eine Fremdkomponente handelt, die wir nicht ändern möchten.

gammatester 30. Jul 2015 10:58

AW: Benutzerdefinierte Compiler-Warnungen
 
Meinst Du vielleicht sowas in der Art?
Delphi-Quellcode:
{$message HINT 'So vielleicht nicht!'}
{$message ERROR 'So nicht!'}
{$message WARN 'So nicht!'}

Der schöne Günther 30. Jul 2015 11:01

AW: Benutzerdefinierte Compiler-Warnungen
 
Aber ihr könnt den Quellcode ändern, oder? Wie wäre es mit einer
Delphi-Quellcode:
deprecated
-Direktive für die Überladung mit dem Default-Parameter?

Scurra 30. Jul 2015 13:21

AW: Benutzerdefinierte Compiler-Warnungen
 
Danke euch beiden für die schnelle Hilfe!

Zitat:

Zitat von gammatester (Beitrag 1310302)
Meinst Du vielleicht sowas in der Art?
Delphi-Quellcode:
{$message HINT 'So vielleicht nicht!'}
{$message ERROR 'So nicht!'}
{$message WARN 'So nicht!'}

Na ja, so wie ich das sehe, besteht hier das Problem, dass ich diese Anweisung immer an die Stelle schreiben muss, an der ich die Methode NodeByAttributeValue aufrufe. Mir geht es darum, den Programmierer, der nicht weiß, dass es an der Methode einen Haken gibt, zu warnen, aber nur, falls er den Default-Parameter beim Methodenaufruf nicht explizit setzt.

Zitat:

Aber ihr könnt den Quellcode ändern, oder? Wie wäre es mit einer deprecated -Direktive für die Überladung mit dem Default-Parameter?
Ja, das können wir. Das mit der deprecated-Direktive ist auch eine gute Idee. Allerdings mit 2 Seiteneffekten: Falls die Methode NodeByAttributeValue von der Fremdkomponente selbst irgendwo verwendet wird, dann sollte ich auch diese Aufrufe alle anpassen, sonst bekomme ich immer Warnungen. Und den Code der Fremdkomponente müssten wir dann natürlich auch ändern (neue Methode hinzufügen). Vllt wäre es dann einfacher, den Default-Parameter zu ändern :)

Ich habe schon erwartet, dass es nicht so funktioniert, wie ich mir das vorgestellt habe, aber vllt. findet ja doch noch jemand eine Lösung. Sonst werden wir wohl den Code der Fremdkomponente ändern müssen ;)

alda 30. Jul 2015 19:46

AW: Benutzerdefinierte Compiler-Warnungen
 
Oder Ihr tauscht das verwendete XML Framework aus :P Darf man fragen um welches es sich hier handelt?

Scurra 30. Jul 2015 21:25

AW: Benutzerdefinierte Compiler-Warnungen
 
Es handelt sich um NativeXml. Mein Chef meinte, am besten wäre es sowieso, wenn man nur einmal aus den xml-Dateien ließt und sich dann ein eigenes Speichermodell generiert, um den Inhalt ständig im Speicher zu halten, da wir viel mit xml-Dateien arbeiten und der Zugriff auf xml-Dateien die Software langsam macht. Aus Mangel an Erfahrung wüsste ich nicht, ob das wirklich so ist.
Allerdings finde ich es schon beeindruckend, dass man durch das Ändern von einem Parameter wie bei der oben genannten Methode die Laufzeit bei manchen Prozessen um einen Faktor von mehr als 2 verkürzen kann. Die Anzahl der Funktionsaufrufe an die Methode konnte ich vom 6-stelligen in den 3-stelligen Bereich reduzieren :D

jfheins 30. Jul 2015 21:40

AW: Benutzerdefinierte Compiler-Warnungen
 
Zitat:

Zitat von Scurra (Beitrag 1310441)
Mein Chef meinte, am besten wäre es sowieso, wenn man nur einmal aus den xml-Dateien ließt und sich dann ein eigenes Speichermodell generiert, um den Inhalt ständig im Speicher zu halten, da wir viel mit xml-Dateien arbeiten und der Zugriff auf xml-Dateien die Software langsam macht. Aus Mangel an Erfahrung wüsste ich nicht, ob das wirklich so ist.

Höchstwahrscheinlich schon. Am besten am Anfang einmal die Datei laden, alles im RAM machen und dann am Ende wieder einmal (am Stück) schreiben. Sofern der RAM die Daten halten kann. Bedenke, Zugriffszeiten auf dem RAM sind so 60 Nanosekunden, auf schnelle SSDs noch 9 Mikrosekunden.

Oder zur besseren Vorstellung: Wenn ein CPU-Zyklus 1 Sekunde lang ist, dauert RAM-Zugriff 180s. Also wenn die Info auf einem Zettel steht (weil du es dir nicht mehr merken konntest) die Zeit wenn du den Zettel im Nebenraum in einem Schrank suchen müsstest.
Die (moderne, schnelle) SSD dauert dann 7,5h! Du kannst den Zettel Papier also schnell aus einer 350km entfernten Stadt abholen ;-) Und das ist eine schnelle SDD. Eine HDD braucht in der Skalierung ein ganzes Jahr um dir den Zettel zu liefern!

Scurra 31. Jul 2015 06:06

AW: Benutzerdefinierte Compiler-Warnungen
 
Danke für die Veranschaulichung. Es wundert mich nur, dass es so lange dauert, denn zum Teil halten wir den Inhalt der xml-Dateien schon im Speicher, also in Form eines xml-Dokuments (TNativeXml). Anscheinend dauert aber der Zugriff auf solche xml-Dokumente immer noch relativ lange, weshalb man wohl z. B. Listen o. ä. verwenden könnte, um den Inhalt der xml-Dateien im Speicher zu halten.

alda 31. Jul 2015 22:31

AW: Benutzerdefinierte Compiler-Warnungen
 
Zitat:

Zitat von jfheins (Beitrag 1310442)
Höchstwahrscheinlich schon. Am besten am Anfang einmal die Datei laden, alles im RAM machen und dann am Ende wieder einmal (am Stück) schreiben. Sofern der RAM die Daten halten kann. Bedenke, Zugriffszeiten auf dem RAM sind so 60 Nanosekunden, auf schnelle SSDs noch 9 Mikrosekunden.

Also die Implementierung sieht mir danach aus, dass bereits alles per LoadFrom* eingelesen wird und somit nicht nochmal extra von Platte gelesen werden muss. Man müsste sich das mal im Detail anschauen, aber abhängig von der Größe und Komplexität der XML-Datei hat man eben abertausende Node & Attribute Objekte die komplett durchsucht werden bis der gewünschte Knoten gefunden wird.

Die Frage ist daher in der Tat (hab gerade leider kein Delphi zur Hand), ob es an der NativeXML Implementierung liegt oder an einem "zu großen" XML.

Im allgemeinen würde ich sowieso grundsätzlich empfehlen beim "in ein XML greifen" mit XPath zu arbeiten - so könntest Du unabhängig von der jeweiligen Implementierung selbst angeben ob rekursiv gesucht werden soll oder nicht (Stichwort Axes). Soweit ich sehe unterstützt NativeXML aber kein Xpath, oder irre ich mich?

Sir Rufo 31. Jul 2015 22:44

AW: Benutzerdefinierte Compiler-Warnungen
 
Dein Chef hat Recht. Wenn du nicht gerade ein XML-Validierungs-Import-Export-Programm schreibst, dann gehört der Umgang mit den XML-Dateien an den Rand der Anwendung und nicht in den Kern (obwohl, selbst dann sogar ;))

Scurra 3. Aug 2015 09:00

AW: Benutzerdefinierte Compiler-Warnungen
 
Zitat:

Soweit ich sehe unterstützt NativeXML aber kein Xpath, oder irre ich mich?
Dazu kann ich leider noch nichts sagen, da ich XPath gerade zum ersten Mal höre ;) So wie ich das auf die Schnelle gelesen habe, stellt XPath Funktionen bereit, um durch xml-Dokumente zu navigieren. Ist das richtig so?

Zitat:

Dein Chef hat Recht. Wenn du nicht gerade ein XML-Validierungs-Import-Export-Programm schreibst, dann gehört der Umgang mit den XML-Dateien an den Rand der Anwendung und nicht in den Kern (obwohl, selbst dann sogar ;))
Na ja, ich ging bisher immer davon aus, dass ein xml-Parser nichts anderes macht, als den Inhalt eines xml-Dokuments in sein eigenes Speichermodell (wie auch immer das aussehen mag) liest. Wenn ich mir selbst einen Parser schreiben würde, dann würde ich das zumindest auf diese Weise machen, d. h. die .xml-Datei wie als Text-Datei betrachten und mir die Knoten, die Struktur usw. mit Listen o. ä. in den Speicher einlesen (mit einer Methode wie LoadFromFile) und wenn ich dann mit Methoden wie "GetNode(Integer)" o. ä. würde ich dann in meinem Speichermodell operieren. Insofern ist mir nicht ganz klar, warum das Arbeiten direkt mit xml-Dateien/xml-Dokumenten langsamer sein soll.

Aber mir fällt auch gerade auf, dass wir durch diese xml-Geschichte vom eigentlichen Thema abkommen ;)

alda 3. Aug 2015 17:45

AW: Benutzerdefinierte Compiler-Warnungen
 
Zitat:

Zitat von Scurra (Beitrag 1310744)
Zitat:

Soweit ich sehe unterstützt NativeXML aber kein Xpath, oder irre ich mich?
Dazu kann ich leider noch nichts sagen, da ich XPath gerade zum ersten Mal höre ;) So wie ich das auf die Schnelle gelesen habe, stellt XPath Funktionen bereit, um durch xml-Dokumente zu navigieren. Ist das richtig so?

XPath ist eine Querylanguage und hat somit eine entsprechende Syntax zum abfragen von XML Strukturen. Schau Dir dazu einfach mal ein Tutorial an (z.B. w3schools)

Zur Veranschaulichung mal ein kleines Beispiel anhand deines Anwendungsfalls:

Gegeben sei folgende XML Struktur
Code:
<?xml version="1.0" encoding="UTF-8"?>
<bookstore>
  <book category="COOKING"/>
  <book category="CHILDREN"/>
  <book category="WEB"/>
  <book category="WEB"/>
  <usedbooks>
    <book category="WEB"/>
    <book category="COOKING"/>
    <book category="MISC"/>
  </usedbooks>
</bookstore>
XPath Query um ..
  • alle Knoten books mit der category "WEB" zu erhalten, allerdings nicht rekursiv (also nur auf bookstore Ebene):
    //bookstore/book[@category="WEB"]
  • alle Knoten books mit der category "WEB" zu erhalten, allerdings rekursiv (somit auch im usedbooks Knoten)
    //bookstore//book[@category="WEB"]

Sir Rufo 3. Aug 2015 18:00

AW: Benutzerdefinierte Compiler-Warnungen
 
Zitat:

Zitat von Scurra (Beitrag 1310744)
Zitat:

Dein Chef hat Recht. Wenn du nicht gerade ein XML-Validierungs-Import-Export-Programm schreibst, dann gehört der Umgang mit den XML-Dateien an den Rand der Anwendung und nicht in den Kern (obwohl, selbst dann sogar ;))
Na ja, ich ging bisher immer davon aus, dass ein xml-Parser nichts anderes macht, als den Inhalt eines xml-Dokuments in sein eigenes Speichermodell (wie auch immer das aussehen mag) liest. Wenn ich mir selbst einen Parser schreiben würde, dann würde ich das zumindest auf diese Weise machen, d. h. die .xml-Datei wie als Text-Datei betrachten und mir die Knoten, die Struktur usw. mit Listen o. ä. in den Speicher einlesen (mit einer Methode wie LoadFromFile) und wenn ich dann mit Methoden wie "GetNode(Integer)" o. ä. würde ich dann in meinem Speichermodell operieren. Insofern ist mir nicht ganz klar, warum das Arbeiten direkt mit xml-Dateien/xml-Dokumenten langsamer sein soll.

Aber mir fällt auch gerade auf, dass wir durch diese xml-Geschichte vom eigentlichen Thema abkommen ;)

Es ist nicht gesagt, dass der alles im Speicher halten muss, kann ja. Um das herauszufinden, müsstest du dir die Implementierung ansehen.

Egal, wie und was, mit ein paar leichtgewichtigen Klassen kannst du die Struktur wesentlich einfacher abbilden und musst nicht den ganzen XML-Ballast mit dir herumschleppen.

Statt dich hier durchzukämpfen um ein Buch anzuhängen:
XML-Code:
<?xml version="1.0" encoding="UTF-8"?>
<bookstore>
  <newbooks>
    <book category="COOKING"/>
    <book category="CHILDREN"/>
    <book category="WEB"/>
    <book category="WEB"/>
  </newbooks>
  <usedbooks>
    <book category="WEB"/>
    <book category="COOKING"/>
    <book category="MISC"/>
  </usedbooks>
</bookstore>
erstellst du dir (hier zwei) Klassen um die benötigte Struktur abzubilden:
Delphi-Quellcode:
TBook = class
  property Category: string;
end;

TBookstore = class
  property NewBookes: TList<TBook>;
  property UsedBooks: TList<TBook>;
end;
Und dann hast du einen Serializer/Desrializer, der dann beim Import/Export zu Tragen kommt:
Delphi-Quellcode:
// Holen
LBookstore := TXmlSerializer.Deserialize<TBookstore>( XmlData );

// Wildeste Verarbeitungen durchführen
LBookstore.NewBooks.Add( TBook.Create() );

// Und so bekommen wir das wieder in eine XML-Datei
TXmlSerializer.Serialize( LBookstore, XmlData );
Nur so vom Anschauen: Was ist deiner Meinung nach schneller (und sogar noch einfacher) in der Verarbeitung?

Dejan Vu 4. Aug 2015 06:11

AW: Benutzerdefinierte Compiler-Warnungen
 
Tippfehler: Statt
Delphi-Quellcode:
property NewBookes: TList<TBook>;
lieber
Delphi-Quellcode:
property NewBooks: TList<TBook>;
.

;-)

Scurra 4. Aug 2015 06:25

AW: Benutzerdefinierte Compiler-Warnungen
 
@alda: Danke für die kurze Einführung. Es hat noch etwas gedauert, bis ich verstanden habe, in welcher Weise man die Such-Anweisungen in den Delphi-Code einbinden muss. Sind die Anfragen mit XPath denn im Allgemeinen schneller als mit (den meisten) anderen Xml-Parsern wie z. B. TXmlDocument?

Zitat:

Nur so vom Anschauen: Was ist deiner Meinung nach schneller (und sogar noch einfacher) in der Verarbeitung?
Ich gehe mal davon aus, dass es eine rethorische Frage ist, denn so sieht dein Code natürlich schneller aus. Allerdings ist dein Code weniger allgemein. Wenn man nun ein anderes xml-Dokument hätte, dann müsste/sollte man andere Klassen bauen. Ich würde es folgendermaßen aufbauen:

Delphi-Quellcode:
TXmlNode = class
private
  FText: string;
  FAttributes: TDictionary<string, string>;
  FChildNodes: TList<TXmlNode>;
  ...
end;

TXmlDoc = class
private
  FRoot: TXmlNode
public
  LoadFromFile(aFileName: string);
  SaveToFile(aFileName: string);
end;
Man müsste dann eben noch Properties zu den Feldern ergänzen und Methoden zum Durchsuchen anbieten. Der Code soll jetzt ja auch nur meine Idee darstellen. In LoadFromFile müsste dann der Wurzel-Knoten inkl. aller Childnodes etc. eingelesen werden. Wenn man es sich ganz einfach machen möchte, verwendet man hier einfach einen bereits vorhandenen Parser (wie z. B. NativeXml, TXmlDocument o. ä.) und liest dann lediglich alle Daten in "meine" Struktur ein.

@Dejan Vu: Ich habe den Tippfehler überlesen ;)

alda 4. Aug 2015 17:41

AW: Benutzerdefinierte Compiler-Warnungen
 
Zitat:

Zitat von Scurra (Beitrag 1310857)
@alda: Danke für die kurze Einführung. Es hat noch etwas gedauert, bis ich verstanden habe, in welcher Weise man die Such-Anweisungen in den Delphi-Code einbinden muss. Sind die Anfragen mit XPath denn im Allgemeinen schneller als mit (den meisten) anderen Xml-Parsern wie z. B. TXmlDocument?

Zur tatsächlichen Geschwindigkeit kann ich nichts sagen, da ich das bisher nie evaluieren musste (bisher gab es keine Notwendigkeit dafür). Ich persönlich verwende dafür auch immer die Winapi.Msxml Implementierung. Das müsstest Du dann für deinen Anwendungsfall evaluieren bzw einfach mal debuggen, wo bei NativeXML ide Zeit verloren geht und warum.

Zitat:

Zitat von Scurra (Beitrag 1310857)
Ich gehe mal davon aus, dass es eine rethorische Frage ist, denn so sieht dein Code natürlich schneller aus. Allerdings ist dein Code weniger allgemein. Wenn man nun ein anderes xml-Dokument hätte, dann müsste/sollte man andere Klassen bauen.

Logisch, wenn sich das korrespondierende XML ändert, muss sich natürlich auch die Zugriffsklasse ändern.

Zitat:

Zitat von Scurra (Beitrag 1310857)
Ich würde es folgendermaßen aufbauen:

Delphi-Quellcode:
TXmlNode = class
private
  FText: string;
  FAttributes: TDictionary<string, string>;
  FChildNodes: TList<TXmlNode>;
  ...
end;

TXmlDoc = class
private
  FRoot: TXmlNode
public
  LoadFromFile(aFileName: string);
  SaveToFile(aFileName: string);
end;
Man müsste dann eben noch Properties zu den Feldern ergänzen und Methoden zum Durchsuchen anbieten. Der Code soll jetzt ja auch nur meine Idee darstellen. In LoadFromFile müsste dann der Wurzel-Knoten inkl. aller Childnodes etc. eingelesen werden. Wenn man es sich ganz einfach machen möchte, verwendet man hier einfach einen bereits vorhandenen Parser (wie z. B. NativeXml, TXmlDocument o. ä.) und liest dann lediglich alle Daten in "meine" Struktur ein.

Diese Ansatz wäre allerdings wieder allgemein und "untypisiert" (das macht ja schon jede XML-Lib so oder ähnlich). Das Beispiel von Sir Rufo ist typisiert auf die von dir verwendete XML-Struktur und ermöglicht dir einen objektorientierten Umgang mit den Daten in Form von Klassen und Objekten - man sieht also von Außen z.B. garnicht, dass diese Daten am Ende in einem XML landen. Vielleicht übersehe ich aber auch nur etwas in deinem Beispiel :P

Sir Rufo 4. Aug 2015 19:38

AW: Benutzerdefinierte Compiler-Warnungen
 
@Scurra

Natürlich ist mein Code nicht mehr allgemein, sondern eben speziell.

Wenn du aber eine Anwendung schreibst, die in einer XML-Datei bestimmte Strukturen erwartet, kannst du dann jede allgemeine XML-Datei verwenden oder auch nur spezielle?

Wenn ich eine Kokosnuss essen möchte, dann kann ich mir

a) Zähne und Kiefer aus Titan einbauen lassen und kann dann sogar zum Nachtisch an einem T-Träger knabbern - wie praktisch

oder

b) mit geeignetem Hilfsmittel (z.B. Hammer) die Kokosnuss in eine leicht zu konsumierende Form umwandeln

Was ich weder bei a) oder b) hinbekomme ist natürlich der umgekehrte Weg zurück zur Kokosnuss.

Scurra 6. Aug 2015 06:30

AW: Benutzerdefinierte Compiler-Warnungen
 
Ok, ich habe es vorher wohl falsch verstanden. Ich dachte, dass man durch solch eine Struktur, wie ich sie in meinem letzten Beitrag beschrieben habe, schneller arbeiten kann, als wenn man sich ein xml-Dokument nur z. B. mittels TXmlDocument im Speicher hält. Im Nachhinein denke ich, dass ich vllt. etwas naiv war, denn wenn man es so schneller implementieren könnte, dann hätte es sicherlich schon jemand gemacht ;)

Jetzt habe ich aber die Idee verstanden, Strukturen zu verwenden, die speziell auf bestimmte xml-Dateien zugeschnitten sind, zu verwenden, um sich dadurch Zeit zu sparen.

Wieder etwas dazu gelernt :) Danke euch beiden!


Alle Zeitangaben in WEZ +1. Es ist jetzt 01:22 Uhr.

Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz