Sven's CA-Visual Objects - Seite

23.11.2012

eMail

eMail

Übersicht | Vorheriger | Nächster

Performance der DBSERVER-Klasse:

Vergleicht man die Performance eines DBSERVER's mit den diskreten DB-Funktionen, so stellt man fest, dass der DBSERVER wesentlich langsamer ist als die diskreten DB-Funktionen und zwar um den Faktor 75 im ungünstigsten Fall.

Woher kommt dieser große Unterschied ?

  1. Die Interne Fehlerbehandlung.
  2. Das ständige aktualisieren eines internen Puffers bei jeder Datensatzbewegung, auch dann, wenn Concurrency gleich NONE ist.
  3. Die nicht dokumentierte ACCESS/ASSIGN DBSERVER:Retries, die bewirkt, dass alle Datenbankoperationen mehrmals wiederholt werden, falls sie beim ersten Versuch fehlschlagen. Hierzu wird eine Funktion __DBSCall() Verwendet, die einen Funktions-Zeiger mit ArgumentListe übergeben bekommt.
  4. Die Notification-Funktionalität der DBSERVER-Klasse.
  5. Das Wechseln des internen Arbeitsbereiches. Dies ist mit Abstand der schlimmste Performancefresser in der DBServer-Klasse. Bei jeder Methode oder Access/Assign-Schnittstelle wird nämlich immer zweimal der Arbeitsbereich gewechselt und das auch wenn überhaupt nur ein Server geöffnet ist. Nach dem Programmstart ist standardmäßig der Arbeitsbereich mit der Nr. 1 eingestellt. Wird nun ein neuer Server geöffnet, so holt sich dieser Server erst einmal einen freien Arbeitsbereich über den Funktionsaufruf VODBSetSelect(-1). Diese Funktion stellt den nächsten freien Arbeitsbereich ein beginnend mit 1023. Der nächste wäre dann 1022 usw. Dieses Verhalten ist dann ganz angenehm, wenn man DBServer und klassische DB-Funktionen mischen würde, was aber ausdrücklich in der Dokumentation nicht empfohlen wird. Tut man es trotzdem, so ist auf dies Weise sichergestellt, dass die Arbeitsbereiche nicht kollidieren, da man die manuell vergeben Arbeitsbereiche in der Regel von 1 an aufwärts vergibt.
    Dies führt dazu, dass selbst dann, wenn nur ein Server geöffnet ist immer ein Arbeitsbereich Wechsel zwischen 1023 und 1 durchgeführt wird und das zweimal pro Methoden- bzw. Access/Assign-Aufruf.

Welche praktischen Schlussfolgerungen lassen sich aus den obigen Feststellungen ziehen ?

  1. Für alle DBServer die mit der Benutzerschnittstelle verbunden sind, muss man nichts beachten. Für diesen Zweck ist die verbleibende Geschwindigkeit eines DBServers immer noch mehr als ausreichend.
  2. Für umfangreichen Berechnungen mit Datenbanken die keine mit der GUI verbundenen Server enthalten hat man prinzipiell zwei Möglichkeiten:
    • Man kann die DB-Funktionen pur benutzen. Dies hat natürlich den Nachteil, dass man sich um die Vergabe der Arbeitsbereiche und Aliase selbst kümmern muss und es handelt sich nun nicht mehr um einen objektorientierten Ansatz.
    • Man erstellt eine neue von DBServer abgeleitete Klasse, in der alle kritischen Methoden überschrieben werden in denen die Performancekiller systematisch entfernt werden. Man verzichtet dabei natürlich auf die Notification und die interne Pufferung und wechselt den Arbeitsbereich nur dann, wenn er nicht dem Arbeitsbereich des Servers entspricht und verzichtet auf ein Rücksetzen vollständig. So ein Server kann natürlich nicht in einem DataWindow eingesetzt werden. Dafür ist die Syntax der Verwendung aber identisch zum DBServer und die Performance ist nur geringfügig langsamer als die reine Lösung mit DB-Funktionen. Ein Beispiel für so einen Server ist die Klasse SEFastServer.
  3. Wenn sich ein DBServer nicht vermeiden läßt, so kann man für zeitkritische Schleifen die DBServer:Eval() Methode verwenden. Diese führt den Codeblock nämlich immer im Arbeitsbereich aus. Ein Wechsel findet nur am Anfang und am Ende statt aber nicht während des Durchlaufs durch die Datenbank.
  4. Für Druckausgaben reicht die Geschwindigkeit der DBServer-Klasse in der Regel völlig.

Ein weiterer interessanter Punkt ist der Performance-Vergleich zwischen folgender Syntax zum Lesen und Schreiben von Datenbankfeldern:

DBServer:FieldGet(#Datum) oder DbServer:Datum:

Selbst wenn der DBServer keine ACCESS/ASSIGN von DATUM hat, also der Aufruf letztlich über DBServer:NoIVarGet/NoIVarPut ausgeführt wird, so ist der Geschwindigkeitsunterschied zwischen beiden Varianten vernachlässigbar. Bei 100000 Aufrufen beträgt der Geschwindigkeitsunterschied gerade einmal 5 hundertstel Sekunden. Das ist so wenig, dass dieser Unterschied auch durch andere Faktoren wie dem GC hervorgerufen werden kann.
Natürlich funktioniert die Variante mit ACCESS/ASSIGN nur bei Datenbankfeldern der Namen nicht mit anderen reservierten Name in Konflikt stehen.

Alias 
AliasSym 
BoF 
ConcurrencyControl 
DbStruct 
Deleted 
Driver 
EoF 
ErrInfo 
FCount 
FieldDesc 
FileSpec 
Filter 
ForBlock 
Found 
Header 
IndexExt 
IndexList 
LastRec 
Lupdate 
Name 
OrderBottomScope 
OrderKeyNo 
OrderKeyVal 
OrderTopScope 
PaintedStructure 
RddName 
Rdds 
ReadOnly 
RecCount 
RecNo 
RecSize 
Retries 
RLockList 
Scope 
Shared 
Status 
Used 
WhileBlock

Am bekanntesten sind sicherlich NAME und STATUS. In all diesen Fällen muss mit DBServer:FieldGet() bzw. DBServer:FieldPut() zugegriffen werden. Die Variante mit ACCESS/ASSIGN hat bei manchen Ausdrücken natürlich Vorteile:

DBServer:Datum += 10

entspricht zum Beispiel

DBServer:FieldPut(#Datum, DBServer:FieldGet(#Datum) + 10)

Auch ist die Lesbarkeit oft einfacher.

 

Home | Kontakt | Impressum | ©2012 Ingenieur-Büro Dipl. Ing. Sven Ebert