Zusammenfassung
Erstellen Sie ein Add-In zum Messen und Vergleichen der Performance von Funktionen.
Techniken
Formuare, Add-Ins, VBA, Klassen
Voraussetzungen
Access 2000 und höher
Beispieldateien
Beispieldatenbank.mdbPerformance.mda
André Minhorst, Duisburg
Die Performance einer Datenbankanwendung ist mithin ein entscheidender Faktor für die Akzeptanz beim Benutzer. Muss dieser bei bestimmten Aktionen länger als ein paar Sekunden auf die Anwendung warten, stellt sich schnell Unzufriedenheit ein. Nun dreht es sich in diesem Beitrag nicht um die Optimierung der Performance, sondern um die Messung der Resultate von Performance-Optimierungen. Und dabei erfahren Sie, wie Sie sich ein nützliches Add-In zur Messung der Performance bauen.
Die Performance einer Anwendung hängt in der Regel von vielen kleinen Faktoren und Bereichen ab, die man getrennt optimiert und entsprechend messen möchte. Für die Messung wird normalerweise die Zeit ermittelt, die die Anwendung für die Erledigung einer bestimmten Aufgabe benötigt.
Das ist auch bei dem Add-In der Fall, das Sie in diesem Beitrag kennen lernen: Es erwartet die Angabe zweier Funktionen mit der gleichen Aufgabe und ermittelt die von den Funktionen benötigte Zeit.
Zeit messen per API
Unter VBA gibt es verschiedene, unterschiedlich genaue Möglichkeiten zur Zeitmessung. Im hier verwendeten Tool kommen zwei API-Funktion namens QueryPerformanceCounter und QueryPerformanceFrequency zum Zuge, die man folgendermaßen deklariert (jeweils in einer Zeile):
Declare Function QueryPerformanceCounter Lib "Kernel32" (X As Currency) As Long Declare Function QueryPerformanceFrequency Lib "Kernel32" (Y As _Currency) As Long
Die erste Funktion weist der als Parameter angegebenen Variablen den Zählerstand eines Prozessor-Counters zu. Wird dieser durch die mit QueryPerformanceFrequency ermittelte Counter-Frequenz geteilt, so erhält man in einer vom jeweiligen Rechner abhängigen Auflösung die Zeit in Sekunden. Um die Zeit zu messen, die eine Funktion benötigt, speichert man einmal den so ermittelten Wert in einer Currency-Variablen, ruft die Funktion auf und berechnet dann aus der Differenz des aktuellen Wertes und dem gespeicherten Wert die benötigten Sekunden.
üblicherweise möchte man mit einer Performance-Messung zwei oder mehr verschiedene Varianten einer Funktion vergleichen. Bevor man hier loslegt, sollte man sich zunächst im Klaren darüber sein, wo sich Optimierungen und damit auch Messungen überhaupt lohnen. Es gibt sicher viele Beispiele für kleine Code-Optimierungen, die eine änderung der benötigten Zeit im Bereich von Sekundenbruchteilen bewirken. Hier kann man den Hebel ansetzen, wenn diese Funktionen sehr, sehr oft aufgerufen werden oder wenn man die Anwendung wirklich bis ins Letzte auf Performance tunen möchte.
Bild 1: Das Performance-Test-Tool im Einsatz
Wer suchet, der findet
Zunächst sollten Sie sich jedoch auf die Suche nach den wirklichen Performance-Schluckern machen und diesen auf den Leib rücken. Bei dieser Suche hilft Ihnen das nachfolgend beschriebene Tool nicht. Sie sollen aber dennoch nicht ohne einen Tipp zu diesem Thema weiterlesen: Verwenden Sie doch einfach die QueryPerformanceCounter-Funktion an verschiedenen Stellen der Anwendung, die Ihnen verdächtig vorkommen.
Schauen Sie zunächst, welche Funktionen viel Zeit kosten, und verfeinern Sie die Suche anschließend, bis Sie optimalerweise die Zeile gefunden haben, die für unerwünscht hohen Zeitverbrauch verantwortlich sind.
Optimieren mit System
Haben Sie eine einzelne oder mehrere zusammenhängende Zeilen gefunden, die für schlechte Performance (mit)verantwortlich sind, haben Sie einen Ansatzpunkt für eine Optimierung. Manchmal lässt sich der Optimierungshebel genau an der gefundenen Stelle ansetzen, manchmal sind aber auch Operationen erforderlich, die sich auf weit größere Teile des Quellcodes beziehen.
Optimierungen quantitativ bewerten
Hier kommt nun endlich das Performance-Test-Tool zum Einsatz. Es sieht wie in Bild 1 aus und lässt sich wie folgt bedienen: Zunächst wählen Sie das Standardmodul oder das Formular aus, in dem sich die zu testende Routine befindet. Nach dem Auswählen des Objekts werden die weiteren Kombinationsfelder mit den im ausgewählten Modul enthaltenen Routinen gefüllt. Ganz wichtig: Die Routinen müssen als öffentliche Routinen deklariert sein! Sonst hat das Tool keine Möglichkeit, die Routinen aufzurufen.
Legen Sie nun zunächst fest, wie oft die Funktionen jeweils aufgerufen werden sollen. Das ist sinnvoll, wenn eine Routine sehr schnell abläuft – also beispielsweise schneller als eine Millisekunde -, aber in der eigentlichen Anwendung etwa in einer Schleife viele Male aufgerufen wird. Beginnen Sie einfach mit einer kleinen Zahl wie 1 oder 10 und arbeiten Sie sich dann in größere Dimensionen vor, wenn das Messergebnis nicht befriedigend ist. Anschließend legen Sie die zu vergleichenden Routinen fest. Hierbei ist Folgendes zu beachten:
Wenn Sie die benötigten Daten eingegeben haben, klicken Sie einfach auf die Schaltfläche Test starten. Das Tool arbeitet die angegebenen Funktionen so oft wie angegeben ab und gibt im Formular die gemessene Zeit sowie die absoluten und relativen Abweichungen von der jeweils anderen Routine aus. Außerdem kennzeichnet es die schnellere und die langsamere Variante mit entsprechenden Symbolen (siehe Bild 1).
Wie testet man Methoden aus Klassenmodulen
Auf die Methoden von Klassenmodulen kann das Tool nicht direkt zugreifen. Der Grund ist, dass die Klasse dazu deklariert und instanziert werden müsste, was zwar prinzipiell möglich ist, aber nur, wenn der Name der Klasse zuvor bekannt ist. Das Tool soll aber als Add-In in beliebigen Datenbanken eingesetzt werden; somit müsste es zur Laufzeit zuvor nicht bekannte Klassen instanzieren. Das funktioniert mit VBA leider nicht, daher müssen Sie zur Messung der Performance von in Klassen verborgenen Methoden einen kleinen Umweg gehen.
Dazu legen Sie in einem Standardmodul eine öffentliche Function-Routine an, die die gewünschte Klasse deklariert und instanziert und die entsprechende Methode aufruft. Das kann beispielsweise wie in Quellcode 1 aussehen.
clsZeitmessungBeispiele ist eine Klasse mit den zwei Methoden EarlyBinding und LateBinding, die in Quellcode 2 abgedruckt sind.
Die erste Funktion TestKlasse1 deklariert und instanziert ein Objekt der Klasse clsZeitmessungBeispiele und ruft die Methode EarlyBinding auf, die Funktion TestKlasse2 erledigt selbiges für die Methode LateBinding.
Um zu vergleichen, welche der beiden Methoden schneller ist, wählen Sie einfach das Standardmodul mdlZeitmessungBeispiele und die beiden Funktionen TestKlasse1 und Testklasse2 im Performance-Mess-Tool aus und starten den Test. Die absolute Differenz zwischen den gemessenen Zeiten ist sicher aussagekräftig, bezieht sich jedoch auf den kompletten Aufruf inklusive Wrapper-Funktion und der eigentlich zu testenden Methode.
Um den relativen Unterschied zwischen den beiden Methoden zu ermitteln, müssen Sie freilich noch einen draufsetzen: Das Tool bietet die Möglichkeit, eine Referenzfunktion anzugeben. Diese Funktion sollte alle Elemente bis auf die eigentlich zu testenden Anweisungen enthalten.
Quellcode 1: Aufruf von in Klassen befindlichen Methoden
Public Function TestKlasse1() Dim obj As clsZeitmessungBeispiele Set obj = New clsZeitmessungBeispiele obj.EarlyBinding Set obj = Nothing End Function Public Function TestKlasse2() Dim obj As clsZeitmessungBeispiele Set obj = New clsZeitmessungBeispiele obj.LateBinding Set obj = Nothing End Function Public Function TestKlasseReferenz() Dim obj As clsZeitmessungBeispiele Set obj = New clsZeitmessungBeispiele ''obj.LateBinding Set obj = Nothing End Function
Quellcode 2: Methoden der Klasse clsZeitmessungBeispiele
Public Sub EarlyBinding() Dim cbr As CommandBar Dim strName As String For Each cbr In Application.CommandBars strName = cbr.Name Next cbr Set cbr = Nothing End Sub Public Sub LateBinding() Dim strName As String Dim cbr As Object For Each cbr In Application.CommandBars strName = cbr.Name Next cbr Set cbr = Nothing End Sub
Bild 2: Genaueres Messen der relativen Differenz durch Verwenden einer Referenzfunktion
Bild 3: Einträge des Kombinationsfeldes cboModule
In Quellcode 1 ist dies die dritte Funktion namens TestKlasseReferenz, die zwar ein Objekt der Klasse clsZeitmessungBeispiele erzeugt, aber keine Methode aufruft.
Die Differenz der gemessenen Zeiten für die Funktionen mit den eigentlichen Aufrufen und des Referenzaufrufs liefert so einen wesentlich genaueren Wert für den relativen Unterschied beim Aufrufen der zu testenden Methoden.
Ende des frei verfügbaren Teil. Wenn Du mehr lesen möchtest, hole Dir ...
den kompletten Artikel im PDF-Format mit Beispieldatenbank
diesen und alle anderen Artikel mit dem Jahresabo