ABAP Tipp - Performance Kettensätze
Schauen wir uns hier einmal die Performance beim Bilden von Kettensätzen mit DATA und FIELD-SYMBOL an. Welche Variante wird bei der Performance vorn liegen?
Inhaltsverzeichnis
Vor nicht allzu langer Zeit waren wir mit einem Kollegen im Austausch, der bereits direkt bei SAP in Projekten gearbeitet hatte. Dort wurde ihm gesagt, er sollten Datendeklarationen nie in Kettensätzen darstellen, da dadurch die Performance leiden würde. Führen wir dazu ein kleines Experiment durch und messen beide Formen.
Einleitung
Was sind eigentlich Kettensätze und wie werden sie gebildet? Es gibt einige Statement in der ABAP-Programmiersprache, mit diesen kann man Sätze bauen, ohne das eigentliche Schlüsselwort zu wiederholen. Dafür setzt man hinter dem Statement einen Doppelpunkt und führt die gleiche Aktion immer wieder aus, trennt die Elemente mit einem Komma. Dazu ein Beispiel:
" Chained statement
DATA:
ld_stream TYPE xstring,
ld_comment TYPE string,
" Single use
DATA ld_stream TYPE xstring.
DATA ld_comment TYPE string.
Vorteil eines Kettensatzes ist, dass man sich Coding spart, weil man das Schlüsselwort weglassen kann. Meist werden solche Statements auch durch den Pretty Printer automatisch formatiert.
Vorbereitung
Um nun der Aussage nachzugehen und einen guten Test zu erzeugen, bereiten wir im ersten Schritt einige Strukturen und Datentypen vor, die wir dann für unseren Testfall verwenden.
TYPES:
td_simple_text TYPE c LENGTH 40,
td_simple_number TYPE p LENGTH 15 DECIMALS 2,
BEGIN OF ts_flat_structure,
key TYPE td_simple_text,
text TYPE string,
number TYPE i,
END OF ts_flat_structure,
tt_flat_standard TYPE STANDARD TABLE OF ts_flat_structure WITH EMPTY KEY,
tt_flat_sorted TYPE SORTED TABLE OF ts_flat_structure WITH UNIQUE KEY key,
BEGIN OF ts_key_value,
key TYPE string,
value TYPE string,
END OF ts_key_value,
tt_key_standard TYPE STANDARD TABLE OF ts_key_value WITH EMPTY KEY,
tt_key_sorted TYPE SORTED TABLE OF ts_key_value WITH UNIQUE KEY key,
BEGIN OF ts_deep,
key TYPE string,
key_value TYPE tt_key_sorted,
flat TYPE tt_flat_sorted,
END OF ts_deep,
tt_deep_standard TYPE STANDARD TABLE OF ts_deep WITH EMPTY KEY,
tt_deep_sorted TYPE SORTED TABLE OF ts_deep WITH UNIQUE KEY key,
BEGIN OF ts_too_deep,
key TYPE string,
structure1 TYPE ts_deep,
structure2 TYPE ts_deep,
table1 TYPE tt_deep_standard,
table2 TYPE tt_deep_sorted,
END OF ts_too_deep,
tt_too_deep_standard TYPE STANDARD TABLE OF ts_too_deep WITH EMPTY KEY,
tt_too_deep_sorted TYPE SORTED TABLE OF ts_too_deep WITH UNIQUE KEY key.
Dazu definieren wir zuerst einige einfache Typen und danach werden es immer komplexere und tiefe Typen, da wir davon ausgehen, dass der Prozess zur Erzeugung von komplexen Typen länger dauert.
Testfall
Bei unserem Test wollen wir einmal die Datendeklaration für DATA und FIELD-SYMBOL validieren, da dies die gängigsten Möglichkeiten sind. Die Testfälle sehen nun so aus, dass wir zuerst einmal einige Daten als Kettensatz generieren und im nächsten Schritt dann die gleichen Daten auf einzelner Ebene. Diese Routinen lassen wir dann x-Mal laufen, um Differenzen untereinander messbar zu machen. Zum Abschluss wird das Ergebnis in die Konsole geschrieben:
GET TIME STAMP FIELD ld_start.
DO c_run_count TIMES.
data_creation_in_one_statement( ).
ENDDO.
GET TIME STAMP FIELD ld_end.
out->write( |DATA - One Statement: { ld_end - ld_start }| ).
DATA
Bei den beiden Methoden generieren wir eine Reihe von einfachen und immer komplexer werdenden Typen. Wir gehen davon aus, dass komplexere Datentypen in der Zeit länger dauern, da dafür mehr Speicher allokiert werden muss:
METHOD data_creation_in_one_statement.
DATA:
ld_text TYPE td_simple_text,
ld_stream TYPE xstring,
ld_comment TYPE string,
ld_value TYPE i,
ld_currency TYPE td_simple_number,
lt_flat_standard TYPE tt_flat_standard,
lt_flat_sorted TYPE tt_flat_sorted,
lt_key_standard TYPE tt_key_standard,
lt_key_sorted TYPE tt_key_sorted,
lt_deep_standard TYPE tt_deep_standard,
lt_deep_sorted TYPE tt_deep_sorted,
lt_too_deep_standard TYPE tt_too_deep_standard,
lt_too_deep_sorted TYPE tt_too_deep_sorted.
ENDMETHOD.
METHOD data_creation_many_statements.
DATA ld_text TYPE td_simple_text.
DATA ld_stream TYPE xstring.
DATA ld_comment TYPE string.
DATA ld_value TYPE i.
DATA ld_currency TYPE td_simple_number.
DATA lt_flat_standard TYPE tt_flat_standard.
DATA lt_flat_sorted TYPE tt_flat_sorted.
DATA lt_key_standard TYPE tt_key_standard.
DATA lt_key_sorted TYPE tt_key_sorted.
DATA lt_deep_standard TYPE tt_deep_standard.
DATA lt_deep_sorted TYPE tt_deep_sorted.
DATA lt_too_deep_standard TYPE tt_too_deep_standard.
DATA lt_too_deep_sorted TYPE tt_too_deep_sorted.
ENDMETHOD.
FIELD-SYMBOL
Bei den Feldsymbolen setzen wir bewusst auf die gleiche Erzeugung der Daten, um den Vergleich untereinander gewährleisten zu können.
METHOD fld_creation_in_one_statement.
FIELD-SYMBOLS:
<ld_text> TYPE td_simple_text,
<ld_stream> TYPE xstring,
<ld_comment> TYPE string,
<ld_value> TYPE i,
<ld_currency> TYPE td_simple_number,
<lt_flat_standard> TYPE tt_flat_standard,
<lt_flat_sorted> TYPE tt_flat_sorted,
<lt_key_standard> TYPE tt_key_standard,
<lt_key_sorted> TYPE tt_key_sorted,
<lt_deep_standard> TYPE tt_deep_standard,
<lt_deep_sorted> TYPE tt_deep_sorted,
<lt_too_deep_standard> TYPE tt_too_deep_standard,
<lt_too_deep_sorted> TYPE tt_too_deep_sorted.
ENDMETHOD.
METHOD fld_creation_many_statements.
FIELD-SYMBOLS <ld_text> TYPE td_simple_text.
FIELD-SYMBOLS <ld_stream> TYPE xstring.
FIELD-SYMBOLS <ld_comment> TYPE string.
FIELD-SYMBOLS <ld_value> TYPE i.
FIELD-SYMBOLS <ld_currency> TYPE td_simple_number.
FIELD-SYMBOLS <lt_flat_standard> TYPE tt_flat_standard.
FIELD-SYMBOLS <lt_flat_sorted> TYPE tt_flat_sorted.
FIELD-SYMBOLS <lt_key_standard> TYPE tt_key_standard.
FIELD-SYMBOLS <lt_key_sorted> TYPE tt_key_sorted.
FIELD-SYMBOLS <lt_deep_standard> TYPE tt_deep_standard.
FIELD-SYMBOLS <lt_deep_sorted> TYPE tt_deep_sorted.
FIELD-SYMBOLS <lt_too_deep_standard> TYPE tt_too_deep_standard.
FIELD-SYMBOLS <lt_too_deep_sorted> TYPE tt_too_deep_sorted.
ENDMETHOD.
Ausführung
Um nun einen Vergleich durchführen zu können, starten wir die Logik mehrere Male und setzen dabei auf einen Schleifendurchlauf von 100000 Durchläufen. In der Praxis heißt das, die Routine wird so oft aufgerufen. Dies könnte man mit einem zentralen Baustein einer Verarbeitungskette vergleichen, wo die Verarbeitung so oft aufgerufen wird. Hier einmal die Ausgabe in der Konsole:
Was bedeuten die Zahlen nun im Detail? Vergleichen wir die einzelnen Statements miteinander, sind die Zahlen recht ähnlich, manchmal sind die Einzelstatements langsamer, manchmal auch der Kettensatz. Von daher machte es auf die Gesamtlaufzeit eines Programms oder einer Verarbeitung keinen Unterschied, wie unsere Daten deklariert werden. Schauen wir uns allerdings einmal die Performance zwischen DATA und FIELD-SYMBOL an, sehen wir doch einen größeren Unterschied. Feldsymbole werden eindeutig schneller zur Verfügung gestellt.
Vollständiges Beispiel
Zum Abschluss des Artikels noch das gesamte Beispiel, damit du den Versuch auch bei dir nachstellen kannst.
CLASS zcl_bs_demo_performance_data DEFINITION PUBLIC FINAL CREATE PUBLIC.
PUBLIC SECTION.
INTERFACES if_oo_adt_classrun.
PROTECTED SECTION.
PRIVATE SECTION.
TYPES:
td_simple_text TYPE c LENGTH 40,
td_simple_number TYPE p LENGTH 15 DECIMALS 2,
BEGIN OF ts_flat_structure,
key TYPE td_simple_text,
text TYPE string,
number TYPE i,
END OF ts_flat_structure,
tt_flat_standard TYPE STANDARD TABLE OF ts_flat_structure WITH EMPTY KEY,
tt_flat_sorted TYPE SORTED TABLE OF ts_flat_structure WITH UNIQUE KEY key,
BEGIN OF ts_key_value,
key TYPE string,
value TYPE string,
END OF ts_key_value,
tt_key_standard TYPE STANDARD TABLE OF ts_key_value WITH EMPTY KEY,
tt_key_sorted TYPE SORTED TABLE OF ts_key_value WITH UNIQUE KEY key,
BEGIN OF ts_deep,
key TYPE string,
key_value TYPE tt_key_sorted,
flat TYPE tt_flat_sorted,
END OF ts_deep,
tt_deep_standard TYPE STANDARD TABLE OF ts_deep WITH EMPTY KEY,
tt_deep_sorted TYPE SORTED TABLE OF ts_deep WITH UNIQUE KEY key,
BEGIN OF ts_too_deep,
key TYPE string,
structure1 TYPE ts_deep,
structure2 TYPE ts_deep,
table1 TYPE tt_deep_standard,
table2 TYPE tt_deep_sorted,
END OF ts_too_deep,
tt_too_deep_standard TYPE STANDARD TABLE OF ts_too_deep WITH EMPTY KEY,
tt_too_deep_sorted TYPE SORTED TABLE OF ts_too_deep WITH UNIQUE KEY key.
CONSTANTS:
c_run_count TYPE i VALUE 100000.
METHODS:
data_creation_in_one_statement,
data_creation_many_statements,
fld_creation_in_one_statement,
fld_creation_many_statements.
ENDCLASS.
CLASS zcl_bs_demo_performance_data IMPLEMENTATION.
METHOD if_oo_adt_classrun~main.
DATA:
ld_start TYPE timestampl,
ld_end TYPE timestampl.
GET TIME STAMP FIELD ld_start.
DO c_run_count TIMES.
data_creation_in_one_statement( ).
ENDDO.
GET TIME STAMP FIELD ld_end.
out->write( |DATA - One Statement: { ld_end - ld_start }| ).
GET TIME STAMP FIELD ld_start.
DO c_run_count TIMES.
data_creation_many_statements( ).
ENDDO.
GET TIME STAMP FIELD ld_end.
out->write( |DATA - Many Statements: { ld_end - ld_start }| ).
GET TIME STAMP FIELD ld_start.
DO c_run_count TIMES.
fld_creation_in_one_statement( ).
ENDDO.
GET TIME STAMP FIELD ld_end.
out->write( |FIELD-SYMBOL - One Statement: { ld_end - ld_start }| ).
GET TIME STAMP FIELD ld_start.
DO c_run_count TIMES.
fld_creation_many_statements( ).
ENDDO.
GET TIME STAMP FIELD ld_end.
out->write( |FIELD-SYMBOL - Many Statements: { ld_end - ld_start }| ).
ENDMETHOD.
METHOD data_creation_in_one_statement.
DATA:
ld_text TYPE td_simple_text,
ld_stream TYPE xstring,
ld_comment TYPE string,
ld_value TYPE i,
ld_currency TYPE td_simple_number,
lt_flat_standard TYPE tt_flat_standard,
lt_flat_sorted TYPE tt_flat_sorted,
lt_key_standard TYPE tt_key_standard,
lt_key_sorted TYPE tt_key_sorted,
lt_deep_standard TYPE tt_deep_standard,
lt_deep_sorted TYPE tt_deep_sorted,
lt_too_deep_standard TYPE tt_too_deep_standard,
lt_too_deep_sorted TYPE tt_too_deep_sorted.
ENDMETHOD.
METHOD data_creation_many_statements.
DATA ld_text TYPE td_simple_text.
DATA ld_stream TYPE xstring.
DATA ld_comment TYPE string.
DATA ld_value TYPE i.
DATA ld_currency TYPE td_simple_number.
DATA lt_flat_standard TYPE tt_flat_standard.
DATA lt_flat_sorted TYPE tt_flat_sorted.
DATA lt_key_standard TYPE tt_key_standard.
DATA lt_key_sorted TYPE tt_key_sorted.
DATA lt_deep_standard TYPE tt_deep_standard.
DATA lt_deep_sorted TYPE tt_deep_sorted.
DATA lt_too_deep_standard TYPE tt_too_deep_standard.
DATA lt_too_deep_sorted TYPE tt_too_deep_sorted.
ENDMETHOD.
METHOD fld_creation_in_one_statement.
FIELD-SYMBOLS:
<ld_text> TYPE td_simple_text,
<ld_stream> TYPE xstring,
<ld_comment> TYPE string,
<ld_value> TYPE i,
<ld_currency> TYPE td_simple_number,
<lt_flat_standard> TYPE tt_flat_standard,
<lt_flat_sorted> TYPE tt_flat_sorted,
<lt_key_standard> TYPE tt_key_standard,
<lt_key_sorted> TYPE tt_key_sorted,
<lt_deep_standard> TYPE tt_deep_standard,
<lt_deep_sorted> TYPE tt_deep_sorted,
<lt_too_deep_standard> TYPE tt_too_deep_standard,
<lt_too_deep_sorted> TYPE tt_too_deep_sorted.
ENDMETHOD.
METHOD fld_creation_many_statements.
FIELD-SYMBOLS <ld_text> TYPE td_simple_text.
FIELD-SYMBOLS <ld_stream> TYPE xstring.
FIELD-SYMBOLS <ld_comment> TYPE string.
FIELD-SYMBOLS <ld_value> TYPE i.
FIELD-SYMBOLS <ld_currency> TYPE td_simple_number.
FIELD-SYMBOLS <lt_flat_standard> TYPE tt_flat_standard.
FIELD-SYMBOLS <lt_flat_sorted> TYPE tt_flat_sorted.
FIELD-SYMBOLS <lt_key_standard> TYPE tt_key_standard.
FIELD-SYMBOLS <lt_key_sorted> TYPE tt_key_sorted.
FIELD-SYMBOLS <lt_deep_standard> TYPE tt_deep_standard.
FIELD-SYMBOLS <lt_deep_sorted> TYPE tt_deep_sorted.
FIELD-SYMBOLS <lt_too_deep_standard> TYPE tt_too_deep_standard.
FIELD-SYMBOLS <lt_too_deep_sorted> TYPE tt_too_deep_sorted.
ENDMETHOD.
ENDCLASS.
Fazit
Wie du an der Messung siehst, scheint der Einfluss auf die Performance so gering zu sein, dass du weiterhin deinen Favoriten wählen kannst. Egal ob Kettensatz oder Einzelstatement, die Performance sollte immer stimmen. Bei manchen Schritten kannst du dir aber überlegen, ob du auf Feldsymbole oder Referenzen setzt. Diese lohnen sich vor allem in Schleifen, wobei hier auch meist in Inline-Deklaration zum Einsatz kommt.
Quelle:
SAP Dokumentation - Kettensatz