ABAP效率优化 LOOP循环嵌套效率分析

前言

        最近优化了一部分程序,基本都是嵌套循环导致的效率低下;ABAP开发时会尽量避免使用嵌套循环,但是实际应用场景绕不开嵌套循环;针对嵌套循环优化,比较常见的优化方式为二分法read+loop index处理,这里通过一个demp程序更好的分析下嵌套循环相关的效率。

实例场景(最后一列为耗时微秒)

        

        1. 针对嵌套循环使用使用二分法read+loop index处理效率优化方面最优,胜于使用排序表

        2. loop index 做条件筛选时占用时间极少,几乎可以忽略不计

        3. 针对内表读取二分法read效率最佳,尽管排序表使用key做关联,默认使用二分法

        4. read 遍历效率远优于loop where 遍历效率

        

        1. read table binary search 随着数据表条目的增加,增加的时间比loop using key更多,但是如下1000000条数据结果,仍然有差距。

        2. 使用hash表 primary key read效率比二分法更优(hash表primary key主键必须唯一,read table 时采用hash算法,因此不适用循环嵌套)

        其它demo输出数据

最终结论

        1. 循环中使用read时,尽量使用二分法,且尽量loop小表,read大表

        2. loop 嵌套循环时,外层循环放小表效率更优

        3. 如果主键确定的read可以考虑使用hash表优化效率

        4. loop where 遍历效率较低,带where条件遍历仅为无where条件遍历时的一半,这是嵌套循环耗费时间的原因之一。

        实际处理嵌套循环时遵循使用效率更高的语句处理大表,较低的处理小表原则

        效率排序

                loop 无条件<loop where <loop using key<loop index

                read <  sorted table read < binary search < hash primary key

        1. 由于系统差异,测试结果存在有一定差异,实践出真知。

        2. hash表sorted key 效果和排序表一致,hash key需要唯一主键,没有计入测试。

       https://mp.csdn.net/mp_blog/creation/editor/125475812

        Demo程序

*&------------------------------------------------------------------*
*& Report ZDEOMO
*&------------------------------------------------------------------*
*&Author : Fireworks                                                *
*&Description : 嵌套循环场景效率测试                                *
*&------------------------------------------------------------------*
REPORT zdemo.

*&---------------------------------------------------------------------*
* DATA Declare
*&---------------------------------------------------------------------*
TYPES: BEGIN OF ty_alv_out,
         test_scenario TYPE text50,
         mode          TYPE i,
         loop_index    TYPE sy-index,
         timestampb    TYPE tzntstmpll,
         timestampe    TYPE tzntstmpll,
         spendtime     TYPE i,
       END OF ty_alv_out.

TYPES: BEGIN OF ty_subroutines,
         form          TYPE char30,
         test_scenario TYPE text50,
         mode          TYPE i,
       END OF ty_subroutines.

TYPES: BEGIN OF ty_test_itab,
         key_char TYPE char2,
         key_num  TYPE numc3,
         int      TYPE sy-index,
         field_1  TYPE text30,
       END OF ty_test_itab.
TYPES: tt_alv_out  TYPE TABLE OF ty_alv_out.

DATA: gt_itab_01     TYPE TABLE OF ty_test_itab,
      gt_itab_02     TYPE TABLE OF ty_test_itab,
      gt_subroutines TYPE TABLE OF ty_subroutines.

DATA gv_mode TYPE i.

DATA: gt_alv_out          TYPE tt_alv_out,
      go_salv             TYPE REF TO cl_salv_table,
      go_display_settings TYPE REF TO cl_salv_display_settings.

*&---------------------------------------------------------------------*
* SELECTION-SCREEN
*&---------------------------------------------------------------------*
SELECTION-SCREEN BEGIN OF BLOCK b1.
PARAMETERS: p_loop TYPE numc3.
PARAMETERS: p_dc01 TYPE i.
PARAMETERS: p_dc02 TYPE i.
SELECTION-SCREEN END OF BLOCK b1.

*&---------------------------------------------------------------------*
* START-OF-SELECTION
*&---------------------------------------------------------------------*
START-OF-SELECTION.

  PERFORM generate_test_data.
  PERFORM set_scenario.
  PERFORM process_scenario.
  PERFORM data_process_befor_output.
  PERFORM output.

*&---------------------------------------------------------------------*
*&      Form  GENERATE_TEST_DATA
*&---------------------------------------------------------------------*
*       text
*----------------------------------------------------------------------*
FORM generate_test_data .

  DATA: lo_random    TYPE REF TO cl_abap_random,
        lv_seed      TYPE i,
        lv_min_limit TYPE i,
        lv_max_limit TYPE i,
        lv_range_num TYPE i.

  DATA: ls_itab_01 TYPE ty_test_itab,
        ls_itab_02 TYPE ty_test_itab.

  DATA: lv_i    TYPE i,
        lv_text TYPE text20.

  CONSTANTS c_char TYPE char50 VALUE '1234567890QWERTYUIOPASDFGHJKLZXCVBNM'.
  CONSTANTS c_text TYPE char80 VALUE '1234567890QWERTYUIOPASDFGHJKLZXCVBNMqwertyuiopasdfghjklzxcvbnm'.

  lv_seed = cl_abap_random=>seed( ).
  lo_random = cl_abap_random=>create( seed = lv_seed ).

  DO p_dc01 TIMES.
    CLEAR: ls_itab_01-key_char,ls_itab_01-key_num.
    ls_itab_01-int = sy-index.
    DO 2 TIMES.
      lv_min_limit = 0  .
      lv_max_limit = 35 .
      lv_range_num = lo_random->intinrange( low = lv_min_limit high = lv_max_limit ).
      ls_itab_01-key_char = ls_itab_01-key_char && c_char+lv_range_num(1).
    ENDDO.

    lv_min_limit = 0  .
    lv_max_limit = 999.
    lv_range_num = lo_random->intinrange( low = lv_min_limit high = lv_max_limit ).
    ls_itab_01-key_num = lv_range_num.

    lv_min_limit = 0  .
    lv_max_limit = 61 .
    lv_range_num = lo_random->intinrange( low = lv_min_limit high = lv_max_limit ).

    ls_itab_01-field_1 = lv_text && ls_itab_01-field_1.
    ls_itab_01-field_1+lv_i(1) = c_text+lv_range_num(1).
    APPEND ls_itab_01 TO gt_itab_01.

    lv_i    = lv_range_num MOD 30.
    lv_text = ls_itab_01-field_1+6(2).
  ENDDO.


  DO p_dc02 TIMES.
    CLEAR: ls_itab_02-key_char,ls_itab_02-key_num.
    ls_itab_02-int = sy-index.
    DO 2 TIMES.
      lv_min_limit = 0  .
      lv_max_limit = 35 .
      lv_range_num = lo_random->intinrange( low = lv_min_limit high = lv_max_limit ).
      ls_itab_02-key_char = ls_itab_02-key_char && c_char+lv_range_num(1).
    ENDDO.

    lv_min_limit = 0  .
    lv_max_limit = 999.
    lv_range_num = lo_random->intinrange( low = lv_min_limit high = lv_max_limit ).
    ls_itab_02-key_num = lv_range_num.

    lv_min_limit = 0  .
    lv_max_limit = 61 .
    lv_range_num = lo_random->intinrange( low = lv_min_limit high = lv_max_limit ).

    ls_itab_02-field_1 = lv_text && ls_itab_02-field_1.
    ls_itab_02-field_1+lv_i(1) = c_text+lv_range_num(1).
    APPEND ls_itab_02 TO gt_itab_02.

    lv_i    = lv_range_num MOD 30.
    lv_text = ls_itab_02-field_1+6(2).
  ENDDO.


ENDFORM.
*&---------------------------------------------------------------------*
*&      Form  SET_SCENARIO
*&---------------------------------------------------------------------*
*       text
*----------------------------------------------------------------------*
FORM set_scenario .

  DATA ls_subroutines TYPE ty_subroutines.

  ls_subroutines-test_scenario  = '循环1表嵌套2表'.
  ls_subroutines-form           = 'LOOP2LOOP'.
  ls_subroutines-mode           = 1.
  APPEND ls_subroutines TO gt_subroutines.

  ls_subroutines-test_scenario  = '循环2表嵌套1表'.
  ls_subroutines-form           = 'LOOP2LOOP'.
  ls_subroutines-mode           = 2.
  APPEND ls_subroutines TO gt_subroutines.

  ls_subroutines-test_scenario  = '循环1表嵌套2排序表'.
  ls_subroutines-form           = 'LOOP2LOOP3'.
  ls_subroutines-mode           = 3.
  APPEND ls_subroutines TO gt_subroutines.

  ls_subroutines-test_scenario  = '循环2表嵌套1排序表'.
  ls_subroutines-form           = 'LOOP2LOOP3'.
  ls_subroutines-mode           = 4.
  APPEND ls_subroutines TO gt_subroutines.

  ls_subroutines-test_scenario  = '循环1表嵌套2表优化'.
  ls_subroutines-form           = 'LOOP2LOOP2'.
  ls_subroutines-mode           = 5.
  APPEND ls_subroutines TO gt_subroutines.

  ls_subroutines-test_scenario  = '循环2表嵌套1表优化'.
  ls_subroutines-form           = 'LOOP2LOOP2'.
  ls_subroutines-mode           = 6.
  APPEND ls_subroutines TO gt_subroutines.


  ls_subroutines-test_scenario  = '循环1表读取2表'.
  ls_subroutines-form           = 'LOOP2READ'.
  ls_subroutines-mode           = 7.
  APPEND ls_subroutines TO gt_subroutines.

  ls_subroutines-test_scenario  = '循环2表读取1表'.
  ls_subroutines-form           = 'LOOP2READ'.
  ls_subroutines-mode           = 8.
  APPEND ls_subroutines TO gt_subroutines.

  ls_subroutines-test_scenario  = '循环1表二分读取2表'.
  ls_subroutines-form           = 'LOOP2READ2'.
  ls_subroutines-mode           = 9.
  APPEND ls_subroutines TO gt_subroutines.

  ls_subroutines-test_scenario  = '循环2表二分读取1表'.
  ls_subroutines-form           = 'LOOP2READ2'.
  ls_subroutines-mode           = 10.
  APPEND ls_subroutines TO gt_subroutines.

  ls_subroutines-test_scenario  = '循环1表key读取2表'.
  ls_subroutines-form           = 'LOOP2READ3'.
  ls_subroutines-mode           = 11.
  APPEND ls_subroutines TO gt_subroutines.

  ls_subroutines-test_scenario  = '循环2表key读取1表'.
  ls_subroutines-form           = 'LOOP2READ3'.
  ls_subroutines-mode           = 12.
  APPEND ls_subroutines TO gt_subroutines.


ENDFORM.
*&---------------------------------------------------------------------*
*&      Form  PROCESS_SCENARIO
*&---------------------------------------------------------------------*
*       text
*----------------------------------------------------------------------*
FORM process_scenario .

  DATA: ls_subroutines TYPE ty_subroutines,
        ls_alv_out     TYPE ty_alv_out.

  DATA: lv_timestamp TYPE tzonref-tstampl.

  DO p_loop TIMES.
    LOOP AT gt_subroutines INTO ls_subroutines.
      ls_alv_out-loop_index    = sy-index.
      ls_alv_out-mode          = ls_subroutines-mode.
      ls_alv_out-test_scenario = ls_subroutines-test_scenario.
      GET TIME STAMP FIELD lv_timestamp.
      ls_alv_out-timestampb = lv_timestamp * 1000000.

      gv_mode = ls_subroutines-mode.
      PERFORM (ls_subroutines-form) IN PROGRAM.

      GET TIME STAMP FIELD lv_timestamp.
      ls_alv_out-timestampe = lv_timestamp * 1000000.
      ls_alv_out-spendtime  = ls_alv_out-timestampe - ls_alv_out-timestampb.
      APPEND ls_alv_out TO gt_alv_out.

    ENDLOOP.
  ENDDO.

ENDFORM.
*&---------------------------------------------------------------------*
*&      Form  DATA_PROCESS_BEFOR_OUTPUT
*&---------------------------------------------------------------------*
*       text
*----------------------------------------------------------------------*
FORM data_process_befor_output .

  DATA: lv_max_time TYPE i,
        lv_min_time TYPE i,
        lv_all_time TYPE p LENGTH 10,
        lt_alv_out  TYPE TABLE OF ty_alv_out,
        ls_alv_out  TYPE ty_alv_out.

  FIELD-SYMBOLS: <ls_alv_out> TYPE ty_alv_out.

  lt_alv_out = gt_alv_out.
  SORT lt_alv_out BY mode.
  ls_alv_out-loop_index = 0.
  MODIFY lt_alv_out FROM ls_alv_out TRANSPORTING loop_index WHERE loop_index <> 0.

  LOOP AT lt_alv_out ASSIGNING <ls_alv_out>.

    IF lv_min_time IS INITIAL.
      lv_min_time = <ls_alv_out>-spendtime.
    ENDIF.

    lv_max_time = nmax( val1 = lv_max_time val2 = <ls_alv_out>-spendtime ).
    lv_min_time = nmin( val1 = lv_min_time val2 = <ls_alv_out>-spendtime ).
    lv_all_time = lv_all_time + <ls_alv_out>-spendtime.

    AT END OF test_scenario.
      CLEAR ls_alv_out.
      ls_alv_out-test_scenario = <ls_alv_out>-test_scenario && ' Average'.
      IF p_loop <= 5 AND p_loop > 0.
        ls_alv_out-spendtime = lv_all_time / p_loop.
      ELSE.
        lv_all_time = lv_all_time - lv_max_time - lv_min_time.
        ls_alv_out-spendtime = lv_all_time / ( p_loop - 2 ).
      ENDIF.
      APPEND ls_alv_out TO gt_alv_out.
      CLEAR: lv_all_time,lv_max_time,lv_min_time.
    ENDAT.
  ENDLOOP.

ENDFORM.
*&---------------------------------------------------------------------*
*&      Form  OUT_PUT
*&---------------------------------------------------------------------*
*       text
*----------------------------------------------------------------------*
FORM output .

  DATA: lx_msg TYPE REF TO cx_salv_msg.

  TRY.
      cl_salv_table=>factory(
        IMPORTING
          r_salv_table = go_salv
        CHANGING
          t_table      = gt_alv_out ).
    CATCH cx_salv_msg INTO lx_msg.
  ENDTRY.

*&------ SALV 展示格式设置
  go_display_settings = go_salv->get_display_settings( ).
  go_display_settings->set_fit_column_to_table_size( cl_salv_display_settings=>true ).

*------ 设置工具条
  go_salv->set_screen_status(
     pfstatus = 'STATUS'
     report = sy-repid
     set_functions = go_salv->c_functions_all ).

*&----- 输出数据
  go_salv->display( ).

ENDFORM.

FORM loop2loop.

  DATA: lt_itab_01 TYPE TABLE OF ty_test_itab,
        lt_itab_02 TYPE TABLE OF ty_test_itab,
        ls_itab_01 TYPE ty_test_itab,
        ls_itab_02 TYPE ty_test_itab.

  IF gv_mode MOD 2 = 1.
    lt_itab_01 = gt_itab_01.
    lt_itab_02 = gt_itab_02.
  ELSE.
    lt_itab_01 = gt_itab_02.
    lt_itab_02 = gt_itab_01.
  ENDIF.

  LOOP AT lt_itab_01 INTO ls_itab_01.
    LOOP AT lt_itab_02 INTO ls_itab_02 WHERE key_char = ls_itab_01-key_char AND key_num = ls_itab_01-key_num.
      ls_itab_01-field_1 = ls_itab_02-field_1.
    ENDLOOP.
  ENDLOOP.

ENDFORM.
FORM loop2loop2.

  DATA: lt_itab_01 TYPE TABLE OF ty_test_itab,
        lt_itab_02 TYPE TABLE OF ty_test_itab,
        ls_itab_01 TYPE ty_test_itab,
        ls_itab_02 TYPE ty_test_itab.

  DATA: lv_from TYPE sy-tabix.

  IF gv_mode MOD 2 = 1.
    lt_itab_01 = gt_itab_01.
    lt_itab_02 = gt_itab_02.
  ELSE.
    lt_itab_01 = gt_itab_02.
    lt_itab_02 = gt_itab_01.
  ENDIF.

  SORT lt_itab_02 BY key_char key_num.
  LOOP AT lt_itab_01 INTO ls_itab_01.
    READ TABLE lt_itab_02 TRANSPORTING NO FIELDS WITH KEY key_char = ls_itab_01-key_char key_num = ls_itab_01-key_num BINARY SEARCH.
    IF sy-subrc = 0.
      lv_from = sy-tabix.
      LOOP AT lt_itab_02 INTO ls_itab_02  FROM lv_from.
        IF ls_itab_02-key_char <> ls_itab_01-key_char OR ls_itab_02-key_num <> ls_itab_01-key_num.
          EXIT.
        ENDIF.
        ls_itab_01-field_1 = ls_itab_02-field_1.
      ENDLOOP.
    ENDIF.
  ENDLOOP.

ENDFORM.

FORM loop2loop3.

  DATA: lt_itab_01 TYPE TABLE OF ty_test_itab,
        lt_itab_02 TYPE TABLE OF ty_test_itab,
        lt_itab_03 TYPE SORTED TABLE OF ty_test_itab WITH NON-UNIQUE KEY primary_key COMPONENTS key_char key_num,
        ls_itab_01 TYPE ty_test_itab,
        ls_itab_02 TYPE ty_test_itab.

  IF gv_mode MOD 2 = 1.
    lt_itab_01 = gt_itab_01.
    lt_itab_02 = gt_itab_02.
  ELSE.
    lt_itab_01 = gt_itab_02.
    lt_itab_02 = gt_itab_01.
  ENDIF.

*  SORT lt_itab_02 BY key_char key_num.
  lt_itab_03 = lt_itab_02.
  LOOP AT lt_itab_01 INTO ls_itab_01.
    LOOP AT lt_itab_03 INTO ls_itab_02 USING KEY primary_key WHERE key_char = ls_itab_01-key_char AND key_num = ls_itab_01-key_num.
      ls_itab_01-field_1 = ls_itab_02-field_1.
    ENDLOOP.
  ENDLOOP.

ENDFORM.

FORM loop2read.

  DATA: lt_itab_01 TYPE TABLE OF ty_test_itab,
        lt_itab_02 TYPE TABLE OF ty_test_itab,
        ls_itab_01 TYPE ty_test_itab,
        ls_itab_02 TYPE ty_test_itab.

  IF gv_mode MOD 2 = 1.
    lt_itab_01 = gt_itab_01.
    lt_itab_02 = gt_itab_02.
  ELSE.
    lt_itab_01 = gt_itab_02.
    lt_itab_02 = gt_itab_01.
  ENDIF.

  LOOP AT lt_itab_01 INTO ls_itab_01.
    READ TABLE lt_itab_02 INTO ls_itab_02 WITH KEY key_char = ls_itab_01-key_char key_num = ls_itab_01-key_num.
    IF sy-subrc = 0.
      ls_itab_01-field_1 = ls_itab_02-field_1.
    ENDIF.
  ENDLOOP.

ENDFORM.
FORM loop2read2.

  DATA: lt_itab_01 TYPE TABLE OF ty_test_itab,
        lt_itab_02 TYPE TABLE OF ty_test_itab,
        lt_itab_03 TYPE TABLE OF ty_test_itab,
        ls_itab_01 TYPE ty_test_itab,
        ls_itab_02 TYPE ty_test_itab.

  IF gv_mode MOD 2 = 1.
    lt_itab_01 = gt_itab_01.
    lt_itab_02 = gt_itab_02.
  ELSE.
    lt_itab_01 = gt_itab_02.
    lt_itab_02 = gt_itab_01.
  ENDIF.

  SORT lt_itab_02 BY key_char key_num.
  lt_itab_03 = lt_itab_02.
  LOOP AT lt_itab_01 INTO ls_itab_01.
    READ TABLE lt_itab_02 INTO ls_itab_02 WITH KEY key_char = ls_itab_01-key_char key_num = ls_itab_01-key_num BINARY SEARCH.
    IF sy-subrc = 0.
      ls_itab_01-field_1 = ls_itab_02-field_1.
    ENDIF.
  ENDLOOP.

ENDFORM.
FORM loop2read3.

  DATA: lt_itab_01 TYPE TABLE OF ty_test_itab,
        lt_itab_02 TYPE TABLE OF ty_test_itab,
        lt_itab_03 TYPE SORTED TABLE OF ty_test_itab WITH NON-UNIQUE KEY primary_key COMPONENTS key_char key_num,
        ls_itab_01 TYPE ty_test_itab,
        ls_itab_02 TYPE ty_test_itab.

  IF gv_mode MOD 2 = 1.
    lt_itab_01 = gt_itab_01.
    lt_itab_02 = gt_itab_02.
  ELSE.
    lt_itab_01 = gt_itab_02.
    lt_itab_02 = gt_itab_01.
  ENDIF.

  SORT lt_itab_02 BY key_char key_num.
  lt_itab_03 = lt_itab_02.
  LOOP AT lt_itab_01 INTO ls_itab_01.
    READ TABLE lt_itab_03 INTO ls_itab_02 WITH KEY key_char = ls_itab_01-key_char key_num = ls_itab_01-key_num.
    IF sy-subrc = 0.
      ls_itab_01-field_1 = ls_itab_02-field_1.
    ENDIF.
  ENDLOOP.

ENDFORM.

 

  • 1
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值