Ich fühlte mich wie in besagtem Film. Es ging um die Frage, warum die Software so lange braucht, obwohl nur wenig Daten zu verarbeiten waren. Zwar hatten sich die Kunden bestimmt schon an die Zwangspause gewöhnt, aber trotzdem sollte das doch mal schneller gehen. Wir könnten einen guten Eindruck machen, wenn es ein wenig schneller ginge mit der Erstellung einer Ausgabedatei.
Es gab eine ältere Fehlermeldung die etwas von einer ganz langen Erstellung einer solchen Ausgabe beschrieb. Die Datenbank lief auf einem eigenen Server. Die Anwendungssoftware auf einem Clientrechner. Für mich deutete das auf ein Problem im Netz hin. Vermutlich war der merkwürdige Datenzugriff schuld. Die Anwendung war auf C-Isam zugeschnitten und nutzte SQL nur zum atomaren Zugriff auf Datensätze.
Das wollte ich zeigen. Es sollte einfach sein. War aber dann ganz anders.
Die Aufgabe
Für den Export der Daten hatten sich unbekannte und längst ausgeschiedene Architekten eine universelle Methode ausgedacht. Die zu exportierenden Daten wurden nicht als Elemente von Datenbankrekords beschrieben, sondern als Inhalte von Maskenfeldern. Ein Baum aus Menüeinträgen bis zum Führungstext spezifizierte die Daten, die der Anwender exportieren wollte. Die Software liest aus der Datenbank alle Daten der ausgewählten Masken aus und filtert die Datenfelder, die angehakt wurden. Dabei kann es durchaus vorkommen, dass ein Datum mehrfach auszugeben wäre. Mit einer besonderen Logik wird darüber buchgeführt.
In den Kommentaren gaben sich die Architekten als Mathematiker zu erkennen. Die Rede war von Objekt- und Metaräumen, die aufeinander abzubilden waren.
Ich hätte das ganz anders gemacht, aber alles umbauen schlagen nur Novizen vor!
Messergebnis
Zum Glück gab es ein Profiling, das mir verraten würde, wo es klemmt. Die in ausgewählten Methoden verbratene Rechenzeit wird über shared memory geloggt.
Erwartet hatte ich einen hohen Anteil an den Funktionen, die aus der Datenbank lasen. Dann könnte ich vielleicht doch ein wenig umbauen.
Verblüfft sah ich hier ein recht zügiges Lesen. Die anschließende Filterung verbrannte den größten Teil.
Das musste ich mir näher ansehen. Ein Brechpunkt musste auf den Anfang der Filterung und dann in Ruhe mal durchschauen. Immer wieder ging es nach GetMaNummer() und GetMdNummer(). Diese klangen wie einfache Funktionen, warfen aber einen aufwändigen Nummernserver an und verbrauchten messbar Zeit. Zurück gaben beide einen const char *.
Allerdings hatte der Programmierer ein Problem mit der Speicherverwaltung unter C++. Das Resultat zeigte auf einen Datenpuffer auf dem Stack.
const char *getXXXX(args)
{
// recht teuerer und aufwändiger Source
static CString result;
....
return result;
}
Zum Glück hatte er nicht das static vergessen. In der ersten Version dieses faux pas gab der Novize einen Zeiger auf den Stack zurück. Das funktionierte auch ganz gut, bis die nächste Funktion aufgerufen und deren Stack die Zeichenfolge überschrieb.
Hier konnte der Wert nicht von der nächsten Funktion überschrieben werden. Aber laut Microsoft von einem anderen CString. Der gecastete Pointer sollte nicht so lange verwendet werden. Genau das hatten meine Vorgänger auch bemerkt. Jedenfalls speicherten sie das Resultat und wunderten sich, dass anstelle des Mitarbeiternamens dort ein Datum sich einschlich.
So verwendeten sie den aufwändig errechneten Zeiger auf eine Zeichenfolge nur einmal und fragten immer wieder getXXX mit den gleichen Parametern auf.
Ist das Problem erst einmal bekannt, ist die Lösung wie immer einfach. Es galt einfach den const char * durch CString zu ersetzen und schon konnte die Ergebnisse der Abfragen wieder verwertet werden.
Hat 30% von der Antwortzeit gebracht!