This is a test message to test the length of the message box.
Login
ABAP Quick Performance INSERT vs VALUE
Erstellt von Software-Heroes

ABAP Tipp - Performance INSERT vs VALUE

506

In diesem Artikel schauen wir uns einmal die Performance der Einfüge-Operationen APPEND, INSERT und VALUE im Hinblick auf Tabellen an und bewerten die Performance und Stabilität bei der Entwicklung.

Werbung


Neben Append und Insert kannst du mittlerweile auch Value verwenden, um Zeilen an eine Tabelle zu hängen oder bei sortierten Tabellen einzufügen. Dazu wollen wir uns einmal die Performance beim Anfügen von Datensätzen anschauen und einen automatisierten Test machen. Zum Vergleich haben wir auch den "alten" Append aufgenommen, um hier vielleicht weitere Unterschiede zu ermitteln.

 

Aufbau

Für die Durchführung des Tests verwenden wir eine Console-Application und legen für jeden Testfall eine Methode an. Die Erzeugung und Haltung der Daten geschieht in der Methode, sodass nach verlassen der Methode der Garbage-Collector die alten Variablen aufräumen kann und so den Speicher freigibt. Die Laufzeitmessung geschieht außerhalb dieser Methode in MAIN. Für den Test haben wir jeweils eine kleine und eine große Tabelle vorgesehen, die jeweils eine unterschiedliche Komplexität darstellen.

TYPES:
  BEGIN OF ts_small,
    integer TYPE i,
    text    TYPE c LENGTH 35,
  END OF ts_small,
  tt_small        TYPE STANDARD TABLE OF ts_small WITH EMPTY KEY,
  tt_small_sorted TYPE SORTED TABLE OF ts_small WITH UNIQUE KEY integer,

  BEGIN OF ts_big,
    integer TYPE i,
    text    TYPE c LENGTH 35,
    ltext   TYPE string,
    amt     TYPE p LENGTH 15 DECIMALS 2,
    curr    TYPE waers,
    tab     TYPE string_table,
  END OF ts_big,
  tt_big        TYPE STANDARD TABLE OF ts_big WITH EMPTY KEY,
  tt_big_sorted TYPE SORTED TABLE OF ts_big WITH UNIQUE KEY integer.

 

Zur Erzeugung der einzelnen gefüllten Zeilen (Struktur), stellen wir zwei Hilfsmethoden zur Verfügung die uns unterschiedliche Zeilen an Hand des Index zur Verfügung stellen, sodass die eingefügten Daten nicht immer gleich sind. Die große Struktur besitzt zusätzlich noch eine tiefe Struktur, da wir eine String-Tabelle zur Verfügung stellen.

METHOD create_small_line.
  rs_result-integer = id_index.
  rs_result-text = |Text for number { id_index }|.
ENDMETHOD.


METHOD create_big_line.
  rs_result-integer = id_index.
  rs_result-text = |Text for number { id_index }|.
  rs_result-ltext = |Some longtext with the number { id_index }. Add this to your table|.
  rs_result-amt = CONV #( id_index ).
  rs_result-curr = 'EUR'.
  rs_result-tab = VALUE #( FOR ld_x = 1 THEN ld_x + 1 WHILE ld_x < 5 ( |Entry { ld_x }| ) ).
ENDMETHOD.

 

Für die Laufzeitmessung verwenden wir die Klasse CL_ABAP_RUNTIME die uns eine Startzeit zur Verfügung stellt und wir diese von den Endzeit subtrahieren können. Diesen Output geben wir dann direkt in die Console von Eclipse aus. An dieser Stelle hast du die Möglichkeit einen Timer mit hoher oder niedriger Genauigkeit zu generieren.

DATA(lo_timer) = cl_abap_runtime=>create_hr_timer( ).

DATA(ld_start) = lo_timer->get_runtime( ).
" Run test method
out->write( |TEST_METHOD_NAME: { lo_timer->get_runtime( ) - ld_start }| ).

 

Test

Die Tests führen wir entsprechend drei Mal durch und bestimmen den Mittelwert als Ergebnis für die Performance Messung. In den Screenshots könnt ihr die einzelnen Werte noch einmal im Detail sehen und wie sie sich für die entsprechenden Tabellengrößen verhalten.

 

Die Ergebnisse der Performance Messung im Durchschnitt sehen dann wie folgt aus. Die Werte wurden entsprechend in Sekunden umgerechnet, um leichter vergleichbar zu sein:

Art des Tests APPEND INSERT VALUE
Standard Tabelle (klein) 5,06 5,06 5,87
Standard Tabelle (groß) 36,3 109,5 116,9
Sortierte Tabelle (klein) 6,60 6,31 7,54
Sortierte Tabelle (groß) 110,4 110,3 115,7

 

Der Value Befehl schneidet in fast allen Fällen etwas schlechter ab und weist eine schlechtere Performance gegenüber dem Insert auf. Zwischen Insert und Append gibt es kaum Unterschiede, außer wir betrachten uns die Zeit beim Anhängen von komplexen und großen Strukturen an eine Standard Tabelle. Hier kann Append mit mehr als 50% Laufzeitgewinn punkten und hängt die anderen beiden Statements ab. Bei der Verwendung einer sortierten Tabelle, kommt es aber kaum noch zu Abweichungen.

 

Hinweis: In unserem Beispiel verwenden wir einen APPEND auf eine sortierte Tabelle mit Schlüssel. Dies funktioniert nur, da der Schlüssel der Tabelle in aufsteigend sortierter Form angehangen wird (SY-INDEX). Für unser Beispiel ist es ausreichend, du solltest aber niemals einen Append an eine sortierte Tabelle vornehmen, da es sonst zu einer Ausnahme (Schlüsselverletzung) kommt.

 

Beispiel

Hier noch einmal die vollständige Klasse zur Nachstellung des Tests. Über die Konstante C_LOOP_VALUE kann die Anzahl der erzeugten Datensätze reguliert werden. Bei ca. 7,5 Millionen Einträgen in der Testtabelle ging dem Prozess aber auf unserem System der Speicher aus und es kam zu einem Abbruch, daher haben wir den Wert auf 5 Millionen gestellt.

CLASS zcl_test_table_performance DEFINITION PUBLIC FINAL CREATE PUBLIC.
  PUBLIC SECTION.
    INTERFACES if_oo_adt_classrun.

  PROTECTED SECTION.
  PRIVATE SECTION.
    CONSTANTS:
      c_loop_value TYPE i VALUE 5000000.

    TYPES:
      BEGIN OF ts_small,
        integer TYPE i,
        text    TYPE c LENGTH 35,
      END OF ts_small,
      tt_small        TYPE STANDARD TABLE OF ts_small WITH EMPTY KEY,
      tt_small_sorted TYPE SORTED TABLE OF ts_small WITH UNIQUE KEY integer,

      BEGIN OF ts_big,
        integer TYPE i,
        text    TYPE c LENGTH 35,
        ltext   TYPE string,
        amt     TYPE p LENGTH 15 DECIMALS 2,
        curr    TYPE waers,
        tab     TYPE string_table,
      END OF ts_big,
      tt_big        TYPE STANDARD TABLE OF ts_big WITH EMPTY KEY,
      tt_big_sorted TYPE SORTED TABLE OF ts_big WITH UNIQUE KEY integer.

    METHODS:
      create_small_line
        IMPORTING
                  id_index         TYPE i
        RETURNING VALUE(rs_result) TYPE ts_small,

      create_big_line
        IMPORTING
                  id_index         TYPE i
        RETURNING VALUE(rs_result) TYPE ts_big,

      test_append_small,
      test_append_big,
      test_insert_small,
      test_insert_big,
      test_value_small,
      test_value_big,

      sort_append_small,
      sort_append_big,
      sort_insert_small,
      sort_insert_big,
      sort_value_small,
      sort_value_big.
ENDCLASS.


CLASS zcl_test_table_performance IMPLEMENTATION.
  METHOD if_oo_adt_classrun~main.
    DATA(lo_timer) = cl_abap_runtime=>create_hr_timer( ).

    DATA(ld_start) = lo_timer->get_runtime( ).
    test_append_small( ).
    out->write( |TEST_APPEND_SMALL: { lo_timer->get_runtime( ) - ld_start }| ).

    ld_start = lo_timer->get_runtime( ).
    test_insert_small( ).
    out->write( |TEST_INSERT_SMALL: { lo_timer->get_runtime( ) - ld_start }| ).

    ld_start = lo_timer->get_runtime( ).
    test_value_small( ).
    out->write( |TEST_VALUE_SMALL : { lo_timer->get_runtime( ) - ld_start }| ).

    ld_start = lo_timer->get_runtime( ).
    test_append_big( ).
    out->write( |TEST_APPEND_BIG  : { lo_timer->get_runtime( ) - ld_start }| ).

    ld_start = lo_timer->get_runtime( ).
    test_insert_big( ).
    out->write( |TEST_INSERT_BIG  : { lo_timer->get_runtime( ) - ld_start }| ).

    ld_start = lo_timer->get_runtime( ).
    test_value_big( ).
    out->write( |TEST_VALUE_BIG   : { lo_timer->get_runtime( ) - ld_start }| ).


    ld_start = lo_timer->get_runtime( ).
    sort_append_small( ).
    out->write( |SORT_APPEND_SMALL: { lo_timer->get_runtime( ) - ld_start }| ).

    ld_start = lo_timer->get_runtime( ).
    sort_insert_small( ).
    out->write( |SORT_INSERT_SMALL: { lo_timer->get_runtime( ) - ld_start }| ).

    ld_start = lo_timer->get_runtime( ).
    sort_value_small( ).
    out->write( |SORT_VALUE_SMALL : { lo_timer->get_runtime( ) - ld_start }| ).

    ld_start = lo_timer->get_runtime( ).
    sort_append_big( ).
    out->write( |SORT_APPEND_BIG  : { lo_timer->get_runtime( ) - ld_start }| ).

    ld_start = lo_timer->get_runtime( ).
    sort_insert_big( ).
    out->write( |SORT_INSERT_BIG  : { lo_timer->get_runtime( ) - ld_start }| ).

    ld_start = lo_timer->get_runtime( ).
    sort_value_big( ).
    out->write( |SORT_VALUE_BIG   : { lo_timer->get_runtime( ) - ld_start }| ).
  ENDMETHOD.


  METHOD create_small_line.
    rs_result-integer = id_index.
    rs_result-text = |Text for number { id_index }|.
  ENDMETHOD.


  METHOD create_big_line.
    rs_result-integer = id_index.
    rs_result-text = |Text for number { id_index }|.
    rs_result-ltext = |Some longtext with the number { id_index }. Add this to your table|.
    rs_result-amt = CONV #( id_index ).
    rs_result-curr = 'EUR'.
    rs_result-tab = VALUE #( FOR ld_x = 1 THEN ld_x + 1 WHILE ld_x < 5 ( |Entry { ld_x }| ) ).
  ENDMETHOD.


  METHOD test_append_small.
    DATA:
      lt_table TYPE tt_small.

    DO c_loop_value TIMES.
      DATA(ls_line) = create_small_line( sy-index ).
      APPEND ls_line TO lt_table.
    ENDDO.
  ENDMETHOD.


  METHOD test_append_big.
    DATA:
      lt_table TYPE tt_big.

    DO c_loop_value TIMES.
      DATA(ls_line) = create_big_line( sy-index ).
      APPEND ls_line TO lt_table.
    ENDDO.
  ENDMETHOD.


  METHOD test_insert_small.
    DATA:
      lt_table TYPE tt_small.

    DO c_loop_value TIMES.
      DATA(ls_line) = create_small_line( sy-index ).
      INSERT ls_line INTO TABLE lt_table.
    ENDDO.
  ENDMETHOD.


  METHOD test_insert_big.
    DATA:
      lt_table TYPE tt_big.

    DO c_loop_value TIMES.
      DATA(ls_line) = create_big_line( sy-index ).
      INSERT ls_line INTO TABLE lt_table.
    ENDDO.
  ENDMETHOD.


  METHOD test_value_small.
    DATA:
      lt_table TYPE tt_small.

    DO c_loop_value TIMES.
      DATA(ls_line) = create_small_line( sy-index ).
      lt_table = VALUE #( BASE lt_table ( integer = ls_line-integer text = ls_line-text ) ).
    ENDDO.
  ENDMETHOD.


  METHOD test_value_big.
    DATA:
      lt_table TYPE tt_big.

    DO c_loop_value TIMES.
      DATA(ls_line) = create_big_line( sy-index ).
      lt_table = VALUE #( BASE lt_table (
        integer = ls_line-integer
        text = ls_line-text
        ltext = ls_line-ltext
        amt = ls_line-amt
        curr = ls_line-curr
        tab = ls_line-tab
      ) ).
    ENDDO.
  ENDMETHOD.


  METHOD sort_append_small.
    DATA:
      lt_table TYPE tt_small_sorted.

    DO c_loop_value TIMES.
      DATA(ls_line) = create_small_line( sy-index ).
      APPEND ls_line TO lt_table.
    ENDDO.
  ENDMETHOD.


  METHOD sort_append_big.
    DATA:
      lt_table TYPE tt_big_sorted.

    DO c_loop_value TIMES.
      DATA(ls_line) = create_big_line( sy-index ).
      APPEND ls_line TO lt_table.
    ENDDO.
  ENDMETHOD.


  METHOD sort_insert_small.
    DATA:
      lt_table TYPE tt_small_sorted.

    DO c_loop_value TIMES.
      DATA(ls_line) = create_small_line( sy-index ).
      INSERT ls_line INTO TABLE lt_table.
    ENDDO.
  ENDMETHOD.


  METHOD sort_insert_big.
    DATA:
      lt_table TYPE tt_big_sorted.

    DO c_loop_value TIMES.
      DATA(ls_line) = create_big_line( sy-index ).
      INSERT ls_line INTO TABLE lt_table.
    ENDDO.
  ENDMETHOD.


  METHOD sort_value_small.
    DATA:
      lt_table TYPE tt_small_sorted.

    DO c_loop_value TIMES.
      DATA(ls_line) = create_small_line( sy-index ).
      lt_table = VALUE #( BASE lt_table ( integer = ls_line-integer text = ls_line-text ) ).
    ENDDO.
  ENDMETHOD.


  METHOD sort_value_big.
    DATA:
      lt_table TYPE tt_big_sorted.

    DO c_loop_value TIMES.
      DATA(ls_line) = create_big_line( sy-index ).
      lt_table = VALUE #( BASE lt_table (
        integer = ls_line-integer
        text = ls_line-text
        ltext = ls_line-ltext
        amt = ls_line-amt
        curr = ls_line-curr
        tab = ls_line-tab
      ) ).
    ENDDO.
  ENDMETHOD.
ENDCLASS.

 

Fazit

Der VALUE Befehl ist zwar sehr umfangreich und bietet viele neue Features, doch kann man bei der Übernahme der Datensätze weiterhin getrost INSERT verwenden, um auch auf der sicheren und stabilen Seite zu bleiben und den Performancevorteil mitnehmen. Von APPEND raten wir mittlerweile grundsätzlich ab, da sich im Laufe der Entwicklung der Tabellentyp verändern kann und man sich somit nachträgliche Änderungen am Quellcode spart. Mehr dazu erfährst du in einem anderen Artikel von uns.


Enthaltene Themen:
TippINSERTVALUEAPPENDPerformance
Kommentare (0)



Und weiter ...

Bist du zufrieden mit dem Inhalt des Artikels? Wir posten jeden Freitag neuen Content im Bereich ABAP und unregelmäßig in allen anderen Bereichen. Schaue bei unseren Tools und Apps vorbei, diese stellen wir kostenlos zur Verfügung.


ABAP in Praxis - String Verarbeitung

Kategorie - ABAP

In diesem praktischen Beispiel schauen wir uns die String Verarbeitung zur Ermittlung der CDS Namen in CamelCase an und wie du das mit ABAP umsetzen kannst.

15.10.2024

ABAP in der Praxis - Test Driven Development

Kategorie - ABAP

Wie funktioniert eigentlich TDD in der Praxis und gibt es einfache Beispiele zum Lernen in ABAP? In dieser Übung gehen wir auf den praktischen Teil ein.

24.09.2024

ABAP in der Praxis - Datenmenge zusammenführen

Kategorie - ABAP

Wir führen wir zwei unterschiedliche Datenmengen in ABAP zusammen, vor allem im Hinblick auf das Moderne ABAP? Eine praktische Aufgabe zum Thema.

17.09.2024

ABAP in der Praxis - Modern ABAP

Kategorie - ABAP

In dieser kleinen Aufgabe schauen wir uns bestehenden klassischen ABAP Quellcode an und versuchen diesen nach Modern ABAP zu optimieren.

27.08.2024

ABAP Tipp - Performance Datenfilterung

Kategorie - ABAP

Welche Anweisung verwendest du in ABAP zur Filterung von internen Tabellen und ist diese performant? In diesem Artikel mehr dazu.

13.08.2024