ABAP Parallel cursor to improve Performance of your SAP reports

Implementing an ABAP parallel cursor in your SAP program has the potential to improve performance of your nested loop by a significant amount

Why is a Parallel Cursor important?

When you loop around an internal table within your ABAP code using a where clause the loop command still processes every row in the table to check if it meets the correct criteria.

This is probably not a problem in many cases as it's the processing within the loop that takes the time rather than the actual loop, but having said that it's always a good idea to avoid any unnecessary processing.

There will also be a number of cases where you have thousands/millions of records but you know that based on your sort criteria only a few of them will match your where clause and be all together somewhere in the middle of the table. The following code demonstrates the use of a parallel cursor, which allows you to go straight to the first of these records and stop processing immediately after you have processed the last one. This can significantly reduce the processing time of your ABAP reports, especially those that have large itab loops or nested loops. The example below reduces the processing time of a very simple nested loop of KEKO and KEPH from 302 seconds to less than 1 seconds.

*&---------------------------------------------------------------------*
*& Report  ZREP_PARALLEL_CURSOR
*&---------------------------------------------------------------------*
*&
*& SAP ABAP functionality
*& **********************
*& Select data from database table into internal table
*& Loop at internal table
*& Loop using Parallel cursor functionality
*& Write info to output report screen
*& SORT internal table
*& Calculate runtime of an ABAP report using GET RUN TIME FIELD
*& Convert GET RUN TIME FIELD to seconds
*&---------------------------------------------------------------------*
REPORT zrep_parallel_cursor.
REPORT zrep_parallel_cursor.
DATA: it_keko TYPE STANDARD TABLE OF keko,
      wa_keko LIKE LINE OF it_keko,
      it_keph TYPE STANDARD TABLE OF keph,
      wa_keph LIKE LINE OF it_keph.
DATA: d_mat_cost  TYPE keph-kst001,
      d_lab_cost  TYPE keph-kst004,
      d_over_head TYPE keph-kst010,
      d_ext_purch TYPE keph-kst014,
      d_misc_cost TYPE keph-kst002,
      ld_starttime TYPE i,
      ld_endtime TYPE i,
      ld_runtime TYPE i.
**************************************************************
* START-OF-SELECTION
START-OF-SELECTION.
  SELECT *
  INTO TABLE it_keko FROM keko.
  SELECT *
  INTO TABLE it_keph FROM keph.
* Table must be sorted to ensure all required records are together
  SORT it_keph BY kalnr kalka bwvar kadky.
* Perform actual processing
  PERFORM get_cost_values.
*----------------------------------------------------------------------*
FORM get_cost_values.
* Determine start position and then process all records for given key
* from that starting point
  GET RUN TIME FIELD ld_starttime.
  LOOP AT it_keko INTO wa_keko.
    clear:d_mat_cost, d_lab_cost, d_over_head, d_ext_purch,d_misc_cost.
* it_keph is sorted on kalnr kalka bwvar kadky.
    READ TABLE it_keph TRANSPORTING NO FIELDS WITH KEY kalnr = wa_keko-kalnr
                               kalka = wa_keko-kalka
                               bwvar = wa_keko-bwvar
                               kadky = wa_keko-kadky BINARY SEARCH.
    IF sy-subrc = 0.
* Loop at itab from first record found (sy-tabix) until record
* no-longer matches your criteria.
      LOOP AT it_keph INTO wa_keph FROM sy-tabix.
        IF  wa_keph-kalnr = wa_keko-kalnr AND wa_keph-kalka = wa_keko-kalka
        AND wa_keph-bwvar = wa_keko-bwvar AND wa_keph-kadky = wa_keko-kadky.
*       Key match
          d_mat_cost = d_mat_cost + wa_keph-kst001.
          d_lab_cost = d_lab_cost + wa_keph-kst004.
          d_over_head = d_over_head + wa_keph-kst010.
          d_ext_purch = d_ext_purch + wa_keph-kst014.
          d_misc_cost = d_misc_cost + wa_keph-kst002 + wa_keph-kst003
                      + wa_keph-kst005 + wa_keph-kst006 + wa_keph-kst007
                      + wa_keph-kst008 + wa_keph-kst009 + wa_keph-kst011
                      + wa_keph-kst012 + wa_keph-kst013 + wa_keph-kst015
                      + wa_keph-kst016 + wa_keph-kst017 + wa_keph-kst018
                      + wa_keph-kst019 + wa_keph-kst020 + wa_keph-kst021
                      + wa_keph-kst022 + wa_keph-kst023 + wa_keph-kst024
                      + wa_keph-kst025 + wa_keph-kst026 + wa_keph-kst027
                      + wa_keph-kst028 + wa_keph-kst029 + wa_keph-kst030
                      + wa_keph-kst031 + wa_keph-kst032 + wa_keph-kst033
                      + wa_keph-kst034 + wa_keph-kst035 + wa_keph-kst036
                      + wa_keph-kst037 + wa_keph-kst038 + wa_keph-kst039
                      + wa_keph-kst040.
        ELSE.
*         End of required records
          EXIT.                                               " Exit loop
        ENDIF.
      ENDLOOP.
    ENDIF.
    IF wa_keko-losgr GE 1.
      d_mat_cost  = d_mat_cost  / wa_keko-losgr.
      d_lab_cost  = d_lab_cost  / wa_keko-losgr.
      d_over_head = d_over_head / wa_keko-losgr.
      d_ext_purch = d_ext_purch / wa_keko-losgr.
      d_misc_cost = d_misc_cost / wa_keko-losgr.
      check not d_mat_cost is INITIAL.
      WRITE:/ d_mat_cost, d_lab_cost, d_over_head, d_ext_purch,d_misc_cost.
    ENDIF.
  ENDLOOP.
  GET RUN TIME FIELD ld_endtime.
  ld_runtime = ld_endtime - ld_starttime. "time in micro seconds
  WRITE:/ ld_runtime, 'micro, nano or what ever seconds'.
  ld_runtime =  ld_runtime / 1000000.        "time in seconds
  WRITE:/ ld_runtime, 'seconds'.
ENDFORM.                               " GET_COST_VALUES

As you can see this code takes less than 1 second to execute



Same code not using parellel cursor increases the runtime from 1 to 302 seconds
If you then run this exact same example ABAP code but without the use of the parallel cursor functionality you can see that the execution time increases significantly. Using the data on my test system which has 36,000 records in KEKO and 57,000 records in KEPH the runtime time goes from 1 second to 302 seconds, which is massive and hopefully demonstrates the importance of using parallel cursors.

*&---------------------------------------------------------------------*
*& Report  ZREP_NONE_PC
*&---------------------------------------------------------------------*
REPORT zrep_none_pc.
DATA: it_keko TYPE STANDARD TABLE OF keko,
      wa_keko LIKE LINE OF it_keko,
      it_keph TYPE STANDARD TABLE OF keph,
      wa_keph LIKE LINE OF it_keph.
DATA: d_mat_cost  TYPE keph-kst001,
      d_lab_cost  TYPE keph-kst004,
      d_over_head TYPE keph-kst010,
      d_ext_purch TYPE keph-kst014,
      d_misc_cost TYPE keph-kst002,
      ld_starttime TYPE i,
      ld_endtime TYPE i,
      ld_runtime TYPE i.
**************************************************************
* START-OF-SELECTION
START-OF-SELECTION.
  SELECT *
  INTO TABLE it_keko FROM keko.
*    UP TO 20 rows.
  SELECT *
  INTO TABLE it_keph FROM keph.
* Table must be sorted to ensure all required records are together
  SORT it_keph BY kalnr kalka bwvar kadky.
* Perform actual processing
  PERFORM get_cost_values.
*----------------------------------------------------------------------*
FORM get_cost_values.
  GET RUN TIME FIELD ld_starttime.
  LOOP AT it_keko INTO wa_keko.
    CLEAR:d_mat_cost, d_lab_cost, d_over_head, d_ext_purch,d_misc_cost.
    LOOP AT it_keph INTO wa_keph WHERE kalnr = wa_keko-kalnr
                                   AND kalka = wa_keko-kalka
                                   AND bwvar = wa_keko-bwvar
                                   AND kadky = wa_keko-kadky.
*       Key match
      d_mat_cost = d_mat_cost + wa_keph-kst001.
      d_lab_cost = d_lab_cost + wa_keph-kst004.
      d_over_head = d_over_head + wa_keph-kst010.
      d_ext_purch = d_ext_purch + wa_keph-kst014.
      d_misc_cost = d_misc_cost + wa_keph-kst002 + wa_keph-kst003
                  + wa_keph-kst005 + wa_keph-kst006 + wa_keph-kst007
                  + wa_keph-kst008 + wa_keph-kst009 + wa_keph-kst011
                  + wa_keph-kst012 + wa_keph-kst013 + wa_keph-kst015
                  + wa_keph-kst016 + wa_keph-kst017 + wa_keph-kst018
                  + wa_keph-kst019 + wa_keph-kst020 + wa_keph-kst021
                  + wa_keph-kst022 + wa_keph-kst023 + wa_keph-kst024
                  + wa_keph-kst025 + wa_keph-kst026 + wa_keph-kst027
                  + wa_keph-kst028 + wa_keph-kst029 + wa_keph-kst030
                  + wa_keph-kst031 + wa_keph-kst032 + wa_keph-kst033
                  + wa_keph-kst034 + wa_keph-kst035 + wa_keph-kst036
                  + wa_keph-kst037 + wa_keph-kst038 + wa_keph-kst039
                  + wa_keph-kst040.
    ENDLOOP.
    IF wa_keko-losgr GE 1.
      d_mat_cost  = d_mat_cost  / wa_keko-losgr.
      d_lab_cost  = d_lab_cost  / wa_keko-losgr.
      d_over_head = d_over_head / wa_keko-losgr.
      d_ext_purch = d_ext_purch / wa_keko-losgr.
      d_misc_cost = d_misc_cost / wa_keko-losgr.
    ENDIF.
    CHECK NOT d_mat_cost IS INITIAL.
    WRITE:/ d_mat_cost, d_lab_cost, d_over_head, d_ext_purch,d_misc_cost.
  ENDLOOP.
  GET RUN TIME FIELD ld_endtime.
  ld_runtime = ld_endtime - ld_starttime. "time in micro seconds
  WRITE:/ ld_runtime, 'micro, nano or what ever seconds'.
  ld_runtime =  ld_runtime / 1000000.        "time in seconds
  WRITE:/ ld_runtime, 'seconds'.
ENDFORM.                               " GET_COST_VALUES

Runtime takes about 302 seconds to execute



What if the internal table was of type SORTED?
So what if you make the internal table SORTED, surely this will also have a positive effect on the performance of nested loops! After all it seems logical that it would.

So what do you think? Will it have a positive effect on the above experiment?? See the SORTED vs. STANDARD internal table comparison example ABAP report for full details and timings.


Related Articles

ABAP Parallel cursor to improve Performance of your SAP reports
ABAP Performance Improvements - Example code and information on various performance enhancements
Performance tuning using GROUPBY
Improving the performance of your ABAP internal table processing
Switch on RTA Dynamically within ABAP Code
Using the Runtime Analysis tool, both manually and from with a program
SAP SQL Trace
Improve ABAP performance with the runtime analysis tcode SAT (SE30)'
Compare performance of various ABAP code using runtime analysis tcode SAT '