ABAP Quick - Performance INSERT vs VALUE
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.
Table of contents
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.