
Recently, I needed to move lines from one table to another. I decided to check which method is the fastest (most performant). Below is my analysis — but first, a few comments on the methodology.
When comparing how AS ABAP processes a set of data (in particular, a table), I realized that the method executed first is always slower. Therefore, I use run_dummy() to eliminate this issue.
I use DO n TIMES for the same reason. If I called each method only once, the first one would take twice as much time as the second (proof at the end of the blog).
I ran each test multiple times and present here the results that consistently occurred.
First, I tested with a sorted table using a unique key, and then with a standard table without a key. Without a key, I couldn’t use FILTER.
The first set of tests was performed on ECC 7.5. The second was repeated on BTP Steampunk, although with a different table type (VBRP and SFLIGHT), so direct comparison of results from ECC and BTP is impossible.
Just paste this into any report and it should run. In the second case, the key definition and the run_filter() method were removed.
CLASS main DEFINITION CREATE PUBLIC.
PUBLIC SECTION.
constants runs TYPE i VALUE 1000.
METHODS: constructor,
run_dummy,
run_loop,
run_for,
run_filter.
PRIVATE SECTION.
DATA all_items TYPE SORTED TABLE OF vbrp WITH UNIQUE KEY vbeln posnr.
ENDCLASS.
CLASS main IMPLEMENTATION.
METHOD constructor.
SELECT * FROM vbrp UP TO 20000 ROWS
INTO TABLE _items
ORDER BY vbeln DESCENDING.
ENDMETHOD.
METHOD run_dummy.
LOOP AT all_items ASSIGNING FIELD-SYMBOL(<item>).
ENDLOOP.
ENDMETHOD.
METHOD run_for.
DATA: selected_items TYPE TABLE OF vbrp.
selected_items = VALUE #( FOR item IN all_items
WHERE ( vbeln = '0080011871' )
( item )
).
ENDMETHOD.
METHOD run_loop.
DATA: selected_items TYPE TABLE OF vbrp.
LOOP AT all_items ASSIGNING FIELD-SYMBOL(<item>) WHERE vbeln = '0080011871'.
APPEND <item> TO selected_items.
ENDLOOP.
ENDMETHOD.
METHOD run_filter.
DATA: selected_items TYPE TABLE OF vbrp.
selected_items = FILTER #( all_items USING KEY primary_key WHERE vbeln = '0080011871' ).
ENDMETHOD.
ENDCLASS.
START-OF-SELECTION.
DATA(main) = new main( ).
do main->runs TIMES.
main->run_dummy( ).
ENDDO.
do main->runs TIMES.
main->run_loop( ).
ENDDO.
do main->runs TIMES.
main->run_filter( ).
ENDDO.
do main->runs TIMES.
main->run_for( ).
ENDDO.
ENDCLASS.
I've made about a dozen runs. This result is representative of all of them (they didn't differ). LOOP is always the fastest, but FILTER is always right behind (difference is insignificant). VALUE FOR however is 50% slower.
I've made probably about 30 test runs. Results varied slightly but all of them can be summed up to two trends presented above.
Initially, LOOP was faster. A few minutes later, FOR overtook it, only to lose its lead again later. I have no idea what caused this fluctuation. In any case, the difference between both methods is minimal.
This test used a different table: the all-time favorite /DMO/FLIGHT. Unfortunately, my BTP version doesn’t offer performance profiling (trace), so I had to use timestamps, which might introduce small measurement errors.
CLASS ycl_test_loops DEFINITION
PUBLIC
FINAL
CREATE PUBLIC .
PUBLIC SECTION.
INTERFACES if_oo_adt_classrun.
CONSTANTS runs TYPE i VALUE 1000.
PRIVATE SECTION.
DATA: all_items TYPE STANDARD TABLE OF /dmo/flight WITH NON-UNIQUE SORTED KEY ki COMPONENTS connection_id,
connid TYPE /dmo/connection_id VALUE '0407'.
METHODS: prepare_data,
run_dummy,
run_loop,
run_for,
run_filter.
ENDCLASS.
CLASS ycl_test_loops IMPLEMENTATION.
METHOD if_oo_adt_classrun~main.
DATA: start TYPE timestampl,
end TYPE timestampl.
me->prepare_data( ).
GET TIME STAMP FIELD start.
DO runs TIMES.
me->run_dummy( ).
ENDDO.
GET TIME STAMP FIELD end.
out->write( |dummy: { end - start }| ).
GET TIME STAMP FIELD start.
DO runs TIMES.
me->run_loop( ).
ENDDO.
GET TIME STAMP FIELD end.
out->write( |loop: { end - start }| ).
GET TIME STAMP FIELD start.
DO runs TIMES.
me->run_for( ).
ENDDO.
GET TIME STAMP FIELD end.
out->write( |for: { end - start }| ).
GET TIME STAMP FIELD start.
DO runs TIMES.
me->run_filter( ).
ENDDO.
GET TIME STAMP FIELD end.
out->write( |filter: { end - start }| ).
ENDMETHOD.
METHOD prepare_data.
DO 500 TIMES. "I have only 40 entries in DB, so 40 * 500 gives me 20k entries
SELECT * FROM /dmo/flight
APPENDING TABLE _items.
ENDDO.
ENDMETHOD.
METHOD run_dummy.
LOOP AT all_items ASSIGNING FIELD-SYMBOL(<item>).
ENDLOOP.
ENDMETHOD.
METHOD run_filter.
DATA: selected_items TYPE TABLE OF /dmo/flight.
selected_items = FILTER #( all_items USING KEY ki WHERE connection_id = connid ).
ENDMETHOD.
METHOD run_for.
DATA: selected_items TYPE TABLE OF /dmo/flight.
selected_items = VALUE #( FOR item IN all_items USING KEY ki
WHERE ( connection_id = connid )
( item )
).
ENDMETHOD.
METHOD run_loop.
DATA: selected_items TYPE TABLE OF /dmo/flight.
LOOP AT all_items ASSIGNING FIELD-SYMBOL(<item>) USING KEY ki
WHERE connection_id = connid.
APPEND <item> TO selected_items.
ENDLOOP.
ENDMETHOD.
ENDCLASS.
The results are in seconds.
dummy: 3.5287930
loop: 1.3220800
for: 1.3824890
dummy: 3.5545590
loop: 1.3494420
for: 1.3473680
As in ECC, there’s no noticeable difference between LOOP and VALUE FOR.
The results are in seconds.
dummy: 3.5353230
loop: 0.4449050
for: 0.5068880
filter: 0.0389550
dummy: 3.5583420
loop: 0.4789230
for: 0.4469660
filter: 0.0380170
dummy: 3.4402150
loop: 0.4193350
for: 0.4578140
filter: 0.0394450
Here things get interesting. Again, FOR and LOOP are very close — but what shocked me is that FILTER is 10x faster! If anyone could double-check my results, please let me know in the comments if I made a mistake.
LOOP and FOR perform almost identically.
When working with standard tables, there’s virtually no difference between LOOP and VALUE FOR. On BTP, LOOP is slightly better.
Using a sorted key reduced search time by ~20–30x on ECC, and ~3x on BTP. On BTP, combining a sorted key with FILTER reduced execution time by a factor of 45.
Interestingly, the SELECT operation is not affected by the key definition — so initializing the sorted key costs nothing. Naturally, modifying the table means the key must be rebuilt, which adds overhead. But if all_items is read-only, a sorted key makes a massive difference.
It’s clear that performance optimizations differ between systems. You can’t write code that’s optimal for both ECC and BTP. And it’s not just about DB storage types — even pure ABAP operations behave differently. On BTP (and likely in all ABAP Cloud environments), FILTER is a game changer. On ECC, it offers little to no advantage—aside from its more concise syntax.
I see no value in the VALUE FOR statement (pun intended). Its syntax is unintuitive and has too many brackets. Also, it’s not debuggable, which makes troubleshooting more complex scenarios a pain in the...
VALUE requires a static type (# is statically derived from context), whereas LOOP supports dynamic types determined at runtime (using field symbols and ASSIGN COMPONENT). In general, we should prefer static typing, but cases where it has to be used, LOOP remains the only way.
FILTER has a clear and concise syntax and is shorter than LOOP. Most importantly, it's significantly faster in ABAP Cloud. It forces us to use sorted keys, while LOOP allows us to be lazy.
On ECC, I used a sorted table. On Steampunk, I used a standard table with a sorted key — so some differences might stem from this.
For standard tables, running a tested method only once causes results to heavily depend on the order of method calls — even if run_dummy() is used:
Someone once said: "The last shall be first, and the first last" ;).
Anyways, when testing performance, be careful not to be fooled by call order.
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
User | Count |
---|---|
6 | |
6 | |
5 | |
4 | |
4 | |
3 | |
3 | |
3 | |
3 | |
3 |