Improve nested loop performance with ABAP

In this article I would like to resume how it is possible to improve performance of our report using some tips.

Consider this scenario:
We have two itabs with Accounting Document Header (BKPF) and Accounting Document Segment (BSEG) and we need to perform some logic in all documents’ positions.
How we can do this in a efficient way?

The worst solution is to perform a loop for each document and then find the position with a full loop in the segments’ table.

LOOP AT lt_bkpf INTO ls_bkpf.
LOOP AT lt_bseg INTO ls_bseg.
lv_counter = lv_counter + 1.
IF ls_bseg-BUKRS = ls_bkpf-BUKRS AND
ls_bseg-BELNR = ls_bkpf-BELNR AND
ls_bseg-GJAHR = ls_bkpf-GJAHR.
** Your logic **
ENDIF.
ENDLOOP.
ENDLOOP.

This is not a crafty approach, we perform a lot of unneeded cycles. We try to improve performance using the WHERE clause in the second LOOP.

LOOP AT lt_bkpf INTO ls_bkpf.
LOOP AT lt_bseg INTO ls_bseg WHERE
BUKRS = ls_bkpf-BUKRS AND
BELNR = ls_bkpf-BELNR AND
GJAHR = ls_bkpf-GJAHR.
lv_counter = lv_counter + 1.
** Your logic **
ENDLOOP.
ENDLOOP.

Now we have significantly reduced the number of cycles and also the execution time. Are we sure that we can do nothing else to decrease execution time?

ABAP LOOP statement has the possibility to use a cursor, in other words, if we have sorted tables we will be able to start looping from the first item, that we are looking for, other items will be in the next positions.

SORT lt_bkpf BY BUKRS BELNR GJAHR.
SORT lt_bseg BY BUKRS BELNR GJAHR.
LOOP AT lt_bkpf INTO ls_bkpf.
READ TABLE lt_bseg TRANSPORTING NO FIELDS WITH KEY
BUKRS = ls_bkpf-BUKRS
BELNR = ls_bkpf-BELNR
GJAHR = ls_bkpf-GJAHR.
CHECK sy-subrc = 0.
LOOP AT lt_bseg INTO ls_bseg FROM sy-tabix.
lv_counter = lv_counter + 1.
** Your logic **
AT END OF GJAHR.
EXIT.
ENDAT.
ENDLOOP.
ENDLOOP.

Using sorted table we can execute READ TABLE statement using the BINARY SEARCH option, this will improve its efficency.

SORT lt_bkpf BY BUKRS BELNR GJAHR.
SORT lt_bseg BY BUKRS BELNR GJAHR.
LOOP AT lt_bkpf INTO ls_bkpf.
READ TABLE lt_bseg TRANSPORTING NO FIELDS WITH KEY
BUKRS = ls_bkpf-BUKRS
BELNR = ls_bkpf-BELNR
GJAHR = ls_bkpf-GJAHR BINARY SEARCH.
CHECK sy-subrc = 0.
LOOP AT lt_bseg INTO ls_bseg FROM sy-tabix.
lv_counter = lv_counter + 1.
** Your logic **
AT END OF GJAHR.
EXIT.
ENDAT.
ENDLOOP.
ENDLOOP.

In this way we have improved our loop execution time, but we can do something else to speed up our algorithm, we can use the field symbols.

SORT lt_bkpf BY BUKRS BELNR GJAHR.
SORT lt_bseg BY BUKRS BELNR GJAHR.
LOOP AT lt_bkpf ASSIGNING <ls_bkpf>
READ TABLE lt_bseg TRANSPORTING NO FIELDS WITH KEY
BUKRS = <ls_bkpf>-BUKRS
BELNR = <ls_bkpf>-BELNR
GJAHR = <ls_bkpf>-GJAHR BINARY SEARCH.
CHECK sy-subrc = 0.
LOOP AT lt_bseg ASSIGNING <ls_bseg> FROM sy-tabix.
lv_counter = lv_counter + 1.
** Your logic **
AT END OF GJAHR.
EXIT.
ENDAT.
ENDLOOP.
ENDLOOP.

Table below summarizes the different algorithms efficiency using increasing amount of data, as you can see the use of these improvements is more important with large itabs.

Nested loop performance

The percentages shows the gain obtained related to the worst case.
Test were performed on SAP ECC 6.0, results may be different on other environment.

Hope this could help you.

Best wishes,
Ivan

12 thoughts on “Improve nested loop performance with ABAP”

  1. Hi hiperion,
    I have the source code, but right now I cannot access to the system to copy and paste; I will share as soon as possible.

    Btw the only code that is missed contains two selects to BKPF and BSEG and the output cycle.

  2. Excellent blog..
    Learned some new tricks here..

    What about looping at item table, then reading the header data inside the loop.
    I believe it is also a good option.
    Let me know what do you think.

    Regards,

    Raveesh

  3. Hi Raveesh,

    you’re right, this could be a better solution, the scope of the blog is to give technical instruments to increment performance, your it’a a logical solution that could be better in this case.

    Here the loop that you suggested.

    SORT lt_bkpf BY BUKRS BELNR GJAHR.
    SORT lt_bseg BY BUKRS BELNR GJAHR.
    LOOP AT lt_bseg ASSIGNING <ls_bseg>.
    AT NEW GJAHR.
    READ TABLE lt_bkpf ASSIGNING <ls_bkpf> WITH KEY
    BUKRS = <ls_bseg>-BUKRS
    BELNR = <ls_bseg>-BELNR
    GJAHR = <ls_bseg>-GJAHR BINARY SEARCH.
    CHECK sy-subrc = 0.
    ENDAT.
    lv_counter = lv_counter + 1.
    ** Your logic **
    ENDLOOP.

    The number of cycles is the same but time perfomance increases considering the access to header itab.

    Great tip!

    For the records here a screen cast with the result:
    http://screencast.com/t/ZDBkZjljMD

    1. Binary search is a different search algorithm, just with a logarithmic complexity instead of the linear complexity of the sequential search.
      Keep in mind that the set as to be sorted to use binary search.

Leave a Reply