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

ABAP Quick - Performance INSERT vs VALUE

1210

In this article we take a look at the performance of the insert operations APPEND, INSERT and VALUE with regard to tables and evaluate the performance and stability during development.



In addition to Append and Insert, you can now also use Value to append rows to a table or to insert them into sorted tables. We want to take a look at the performance when adding data sets and do an automated test. For comparison, we have also included the "old" append in order to perhaps determine further differences here.

 

Structure

We use a console application to carry out the test and create a method for each test case. The generation and storage of the data takes place in the method, so that after leaving the method, the garbage collector can clean up the old variables and thus free the memory. The runtime measurement takes place outside of this method in MAIN. We have provided a small and a large table for the test, each of which represents a different level of complexity.

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.

 

To generate the individual filled lines (structure), we provide two auxiliary methods that provide us with different lines based on the index, so that the inserted data are not always the same. The large structure also has a deep structure because we provide a string table.

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.

 

For the runtime measurement we use the class CL_ABAP_RUNTIME which gives us a start time and we can subtract this from the end time. We then output this output directly to the Eclipse console. At this point you have the option to generate a timer with high or low accuracy.

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

We carry out the tests three times and determine the mean value as the result for the performance measurement. In the screenshots you can see the individual values again in detail and how they behave for the corresponding table sizes.

 

The results of the performance measurement on average then look as follows. The values have been converted into seconds to make it easier to compare:

Type of test APPEND INSERT VALUE
Standard table (small) 5,06 5,06 5,87
Standard table (big) 36,3 109,5 116,9
Sortierte table (small) 6,60 6,31 7,54
Sortierte table (big) 110,4 110,3 115,7

 

In almost all cases, the Value command does a little worse and has a worse performance than the insert. There are hardly any differences between insert and append, unless we look at the time when appending complex and large structures to a standard table. Here Append can score with more than 50% gain in runtime and depends on the other two statements. When using a sorted table, there are hardly any deviations.

 

Hint: In our example we use an APPEND on a sorted table with a key. This only works because the key is appended to the table in ascending order (SY-INDEX). For our example it is sufficient, but you should never append a sorted table, otherwise an exception (key violation) will occur.

 

Example

Here again the full class to simulate the test. The number of generated data records can be regulated via the constant C_LOOP_VALUE. With around 7.5 million entries in the test table, the process ran out of memory on our system and it was aborted, so we set the value to 5 million.

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.

 

Conclusion

The VALUE command is very extensive and offers many new features, but you can still confidently use INSERT when transferring the data records in order to stay on the safe and stable side and take the performance advantage with you. In the meantime, we generally advise against APPEND, as the table type can change in the course of development, thus saving subsequent changes to the source code. You can find out more about this in another article from us.


Included topics:
QuickINSERTVALUEAPPENDPerformance
Comments (0)



And further ...

Are you satisfied with the content of the article? We post new content in the ABAP area every Friday and irregularly in all other areas. Take a look at our tools and apps, we provide them free of charge.


ABAP in Practice - String Processing

Category - ABAP

In this practical example we look at the string processing to determine the CDS names in CamelCase and how you can implement this with ABAP.

10/15/2024

ABAP in Practice - Test Driven Development

Category - ABAP

How does TDD actually work in practice and are there simple examples for learning in ABAP? In this exercise we will look at the practical part.

09/24/2024

ABAP in Practice - Merge data sets

Category - ABAP

How do we merge two different data sets in ABAP, especially with regard to Modern ABAP? A practical task for this topic.

09/17/2024

ABAP in Practice - Modern ABAP

Category - ABAP

In this small task we look at existing classic ABAP source code and try to optimize it according to Modern ABAP.

08/27/2024

ABAP Quick - Performance Data Filtering

Category - ABAP

Which statement do you use in ABAP to filter internal tables and is it performant? Read more in this article.

08/13/2024