35 Jahre nach der Ersteinführung der AS/400 haben die aktuellen Maschinen Leistungsoptionen, von denen man in der Anfangsphase nur träumen konnte. Aber das Phänomen stetig steigender Leistung ist ja nicht nur auf Power beschränkt, sondern findet sich mehr oder weniger plattformunabhängig wieder. Doch auch die schnellsten Maschinen können durch die wachsenden Datenmengen an Grenzen gebracht werden. Dabei liegt es dann häufig auch an dem Geschick der Anwendungsentwickler, wie diese beispielsweise Programme gestalten und gezielte und optimierte Datenzugriffe durchführen.
Einer der Klassiker unter den RPG Programmen ist die Leseschleife zum Verarbeiten von Datensätzen. Diese finden sich in den meisten Anwendungen. Je größer die zu verarbeitenden Mengen an Datensätzen werden, so wichtiger ist hier auch die „richtige“ Zugriffsmethode auf die betreffenden Datensätze.
Selbstverständlich nutzen wir als erfahrene RPG Anwendungsentwickler gezielt logische Dateien für den optimalen Zugriff auf das gewünschte Datensubset. Die SETLL und nachfolgende READE Leseschleife sind Ihnen sicher zigfach bekannt.
Wussten Sie aber, dass damit verbunden auch eine hohe Last an I/O Anweisungen auf der Maschine einhergeht, die in Masse und Kombination auch schnellste und neueste Technik auszureizen.
Aber wir haben ja IBM i – und SQL – und RPG. Und jetzt nutzen wir das Gute aus den 3 Bereichen:
Nehmen wir dazu ein Beispiel einer Umsatzstatistikdatei. Die Auswertungen dauern auch auf einer schnellen Maschine noch verhältnismäßig lange. Der Grund im Wesentlichen: eine Hohe Anzahl an I/O. Diese war in einem Beispiel bei dem Programmablauf bei zirka 500.000 I/O. Die Laufzeit zirka eine Minute. Alleine durch das Austauschen der READE Schleife und dem Ersatz von SQL Fetch konnte für die unveränderte Datei eine Reduzierung der I/Os auf 21 erreicht werden. Die Laufzeit: Statt einer Minute gerade einmal 6 Sekunden.
In unserem Beispielprogramm wollen wir eben diese Umsatzstatistikdatei (UmsatzTbl) für eine Firma verarbeiten.
Die Firmennummer übergeben wir dabei an das Programm, das dann mit diesem Eingangsparameter alle Datensätze in der Umsatzdatei des Vorjahres verarbeitet. Dazu nutzen wir eine logische Datei und deren Schlüsselfeld „Firma“.
Die Leseschleife ist klassisch mit dem Positionieren mittels SETLL und einer Schleife und den READE Anweisungen realisiert.

Mit dem geschickten Einsatz von SQL als Alternative zu RPG Anweisungen können wir einige I/O Operationen sparen. Diese sind nicht nur performanceintensiv, sondern können bei der Verarbeitung von größeren Datenmengen auch Anzahlen von zig 100.000 oder auch Millionen I/O Transaktionen bedeuten. Und genau das kann auch schnellste Maschinen belasten. Wir nutzen die Systemgrenze von derzeit 16 MB für Datenstrukturen zu Gunsten der Performance nach Bedarf aus.
Unser RPG ändern wir dahingehend ab, dass wir die nativen Leseprozesse durch optimiertes SQL ersetzen. Damit lesen wir dann die betreffenden Datensätze blockweise, reduzieren damit die I/O Last, verlagern einen Teil der Arbeit an die Stellen, die als Performancebooster genutzt werden können, und beschleunigen damit nicht nur die Anwendung, sondern entlasten die Maschine, indem wir unnötige multiple Aktionen, die unweigerlich mit den nativen Lesezugriffen verbunden sind, eliminieren.
Zudem können wir hier auch bei Bedarf gezielt nur bestimmte Felder oder Spalten aus der zu verarbeitenden Datei nutzen – aber das ist noch ein weiteres Thema. Gehen wir in einfachsten Fall davon aus, dass wir „nur“ die hohe Anzahl an I/O Transaktionen zu verringern versuchen.
Ersetzen wir nun also die nativen RPG Leseroutinen durch einfache SQL Derivate mit SQL Cursor.

Der Vorteil hierbei besteht darin, dass ein direkter Zusammenhang zwischen der Anzahl der in einem Programm ausgeführten I/O-Vorgänge und der Geschwindigkeit besteht, mit der dieses Programm seine Aufgaben ausführen kann.
Als OEM Programm beenden wir nach getaner Arbeit das PGM durch *INLR – damit werden dann alle von dem Programm reservierten Speicher und Ressourcen freigegeben.
Rufen wir das Programm für einen weiteren Mandanten auf, dann werden die Datei zunächst wieder geöffnet, Speicherplatz reserviert und initialisiert und so weiter. Ein Klassiker halt.
Auch die zuletzt genannten RPG Spezifika wollen wir anpassen – und verhindern, dass bei Mehrfachaufrufen das Programm beendet wird und die Ressourcen freigegeben werden – denn auch das ist ein lastauslösender Vorgang. Also nutzen wir in dem Zusammenhang nun die Aktivierungsgruppen dahingehend, dass unser Programm nun nicht mehr in der Standardaktivierungsgruppe ausgeführt wird und somit bei Mehrfachaufrufen die vorbereitenden Tätigkeiten in unserem RPG entfallen und damit wieder genutzt werden können. Das bedingt dann aber, dass wir uns dann „irgendwann“ darum kümmern müssen, nach getaner Arbeit auch wieder aufzuräumen.
Als erstes füge ich in diesem Zusammenhang die RPG Steueranweisung ctl-opt dftactgrp(*No) ein – siehe auch Zeile 21 in dem vorhergehenden Codebeispiel.
Damit muss dann zum Ende der Programmausführung zwar sichergestellt werden, dass die Dateien geschlossen und die Speicherbereiche bereinigt werden. Details zu den Aktivierungsgruppen finden Sie in einem weiteren Artikel.
Die 16 MB Größenbeschränkung für Arrays können wir so direkt im RPG nicht angeben, aber wir tasten uns durch die Dim Angabe auf der Ebene der DS Beschreibung an einen Idealwert heran. Natürlich können wir diesen auch berechnen. Schauen Sie sich dazu auch das folgende
Codesegment an – hier habe ich einmal bis zu 25.000 zu verarbeitende Vorkommen definiert (siehe Zeile 25 in dem vorhergehenden Codebeispiel).
Diesen in DS definierten Wert nutzen wir dann als Maximalwert bei der SQL Ausführung für die Anzahl der zu lesenden Einträge mittels SQL Fetch.
Es gibt verschiedene Möglichkeiten, die Arrayinformationen im RPG weiter zu verarbeiten. Hier seien qualifizierte und nicht qualifizierte Datenstrukturen als Beispiele genannt.
Schauen Sie sich die Zeile 43 im Programmcode an: Durch das Verschieben dieser Dimension des Arrays in die nicht qualifizierte Datenstruktur befinden sich jetzt alle Felder in meiner UmsatzTbl im Speicher und stehen somit performant zur Verfügung.
Das sind die Schleifenbestimmungen. In der nun folgenden Abbildung sehen Sie die eigentlichen SQL Zugriffe:

Um diese nutzen zu können, müssen wir zunächst einen Cursor definieren. Da wird den Inhalt der Umsatzdatei nur lesen wollen, teilen wir das dem Kompiler an dieser Stelle mit „For Read Only“ mit.
Weiterhin geben wir bei der Cursordefinition auch die Commitangabe (in dem Fall NC) an.
Wenn wir schon dabei sind, Performance zu optimieren, dann sollte es zumindest erwähnt werden:
Die SQL Select Anweisung können wir in den unterschiedlichsten Ausprägungen einsetzen. So zum Beispiel wahlweise nur die im weiteren Verlauf benötigten Felder oder Spalten gezielt zu ermitteln (was sich bei Massendaten wieder positiv auf die Performance auswirken kann) oder, wie in dem Beispiel gezeigt, alle Felder oder Spalten analog dem READ Prozess für die Verarbeitung bereitzustellen.
Vor dem ersten Einlesen (Fetch) öffnen wir den Cursor mit der Open Anweisung (siehe Zeile 72 im vorhergehenden Programmcode).
Die eigentliche Leseschleife habe ich in eine separate Prozedur gepackt. Das ist natürlich Entwicklergeschmackssache und hat keine Bedeutung für die Ausführungsgeschwindigkeit.

Die für uns wirklich entscheidende Zeile ist die Zeile 98. Denn hier reduzieren wir die I/O auf das Minimum – denn durch die Angabe von 25.000 Rows (siehe Zeile 25 in Abb. 2) ermittelt das Fetch Statement bis zu 25.000 Datensätze auf einmal.
Die einzelnen Programmausschnitte sind nur fragmentarisch gehalten. Die eigentliche Verarbeitungslogik fehlt hier gänzlich, denn sie würde die Übersicht erschweren.
Wie Sie sehen kann man durch den geschickten Austausch von nativen Leseroutinen in RPG durch entsprechende SQL Anweisungen viel aus den RPG Anwendungen herausholen.
Viel Spaß beim Testen!
Der Autor Jörg Zeig war freiberuflich im System i Umfeld als Berater tätig und schrieb regelmäßig für den TechKnowLetter. Leider verstarb er im Dezember 2024 überraschend.
Für 88 Euro gibt’s hier sechs Monate lang tiefgreifendes IBM i und SQL Wissen. Hier kann man abonnieren.