Technology Blog Posts by Members
Explore a vibrant mix of technical expertise, industry insights, and tech buzz in member blogs covering SAP products, technology, and events. Get in the mix!
cancel
Showing results for 
Search instead for 
Did you mean: 
Michał_Biegun
Explorer
2,263

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.

 

Methodology

  1. 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.

  2. 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).

  3. I ran each test multiple times and present here the results that consistently occurred.

  4. 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.

  5. 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.

 

ECC tests

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.

Results: sorted table, 1000 executions

Micha_Biegun_0-1744417035935.png

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.

Results: standard table, 1000 executions

Micha_Biegun_1-1744417133596.png

Micha_Biegun_2-1744417144037.png

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.

 

BTP Steampunk Test

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.

Results: Standard Table, 1000 Executions

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.

Results: Sorted key, 1000 Executions

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.

 

Conclusions

On ECC with sorted keys:

  • LOOP was consistently the fastest,
  • FILTER was nearly as fast — arguably equivalent,
  • VALUE FOR was 50% slower (still acceptable, but noticeably slower)

On BTP with sorted keys:

  • LOOP and FOR perform almost identically.

  • FILTER is in a league of its own, being over 10 times faster.

Others:

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.

 

Discussion

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.

Proof for Point 2 in Methodology

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:

Micha_Biegun_0-1744568216811.png

Micha_Biegun_1-1744568227052.png

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.

9 Comments
Labels in this area