Ускоряване на дълго работещ цикъл в ABAP

Когато анализирах проблем с производителността в скрипт за експортиране на SAP (SAP R/3, 4.06b), открих следния код, който се изпълнява около 10 минути в тестовата система. Може да е малко по-бърз в производството, но не мога да го тествам там.

LOOP AT ZMARD.
  LOOP AT ZCOMB.
       IF ZCOMB-MATNR = ZMARD-MATNR.
          IF ZCOMB-LGORT = ZMARD-LGORT.
           IF ZCOMB-IND = ' '.
             IF ZCOMB-PLUMI = '+'.
                ZMARD-LABST = ZMARD-LABST + ZCOMB-MNG01.
             ELSEIF ZCOMB-PLUMI = '-'.
                ZMARD-LABST = ZMARD-LABST - ZCOMB-MNG01.
             ENDIF.                          "PLUMI
          ENDIF.                             "IND
        ENDIF.                               "LGORT
      ENDIF.                                 "MATNR
  ENDLOOP.
  IF ZMARD-LABST < 0.
     ZMARD-LABST = 0.
  ENDIF.
  WRITE ZMARD-LABST DECIMALS 0 TO ZMARD-ZLABST.
  MODIFY ZMARD.
ENDLOOP.

Имате ли съвет как тези цикли могат да бъдат оптимизирани/сглобени в един единствен цикъл?


person dhh    schedule 23.06.2015    source източник
comment
Сигурни ли сте, че LOOP (и командата MODIFY) е тясното място? Какво ще кажете за избора на данни на ZMARD и ZCOMB на първо място?   -  person Mikael G    schedule 29.06.2015
comment
Да @MikaelG - изборът на данни преди е ок.   -  person dhh    schedule 29.06.2015


Отговори (5)


Здравейте, има някои възможности.

Първо: Можете да кондиционирате вътрешния цикъл с "where outer looped struct-field" = "inner loop table field".

Второ: Можете да използвате хеширани таблици. Но те са по-важни за таблицата за четене.

person icbytes    schedule 23.06.2015
comment
Не съм сигурен, че хешираната таблица съществува в 4.06b.. Двоичните търсения на сортирани таблици определено съществуват. - person tomdemuyt; 23.06.2015
comment
Трябва да призная, че не знам откога хешираните таблици са налични, но като не чета за нито една версия във въпроса на op, нека ги считаме за безполезни в тази дискусия. - person icbytes; 23.06.2015
comment
Благодаря ви много - най-накрая добавих where ... към няколко цикъла, за да избегна промяната на твърде много от (работещата и тествана) кодова база. Работата сега изглежда трябва да бъде по-бърза. Btw - има ли някаква възможност за извършване на измерване на времето в старата SAP система? - person dhh; 24.06.2015
comment
Мисля, че se30 може вече да съществува в 4.06b за анализ на производителността - person Esti; 24.07.2015

Това е класически случай, когато две таблици трябва да бъдат пресечени. Кодът във вашия пример е един от най-лошите начини да го направите и може да бъде значително подобрен. Ето една интересна статия за това .

И ето това е реализирано за вашия пример.

Предположения: 1. ZMARD няма дублиращи се записи за комбинация MATNR LGORT; 2. Редът на ЗМАРД не е от значение за отчета;

DATA: IX_MARD TYPE I,
      IX_COMB TYPE I.

DEFINE READ_NEXT.
  ADD 1 TO IX_&1.
  READ TABLE Z&1 INDEX IX_&1.
  IF SY-SUBRC NE 0.
    IX_&1 = -1.
  ENDIF.
END-OF-DEFINITION.

" It would be better if these sorts were done
" in the SELECT of the data
SORT ZMARD BY MATNR LGORT.
SORT ZCOMB BY MATNR LGORT.

IX_MARD = IX_COMB = 0.
READ_NEXT: MARD, COMB.

WHILE IX_MARD GT 0 AND IX_COMB GT 0.

  IF ZMARD-MATNR EQ ZCOMB-MATNR AND
     ZMARD-LGORT EQ ZCOMB-LGORT.
    " Match between MARD and COMB
    IF ZCOMB-IND = ' '.
      IF ZCOMB-PLUMI = '+'.
         ZMARD-LABST = ZMARD-LABST + ZCOMB-MNG01.
      ELSEIF ZCOMB-PLUMI = '-'.
         ZMARD-LABST = ZMARD-LABST - ZCOMB-MNG01.
      ENDIF.                          "PLUMI
    ENDIF.                             "IND

    READ_NEXT: COMB.

  ELSEIF ZMARD-MATNR LT ZCOMB-MATNR OR
         ( ZMARD-MATNR EQ ZCOMB-MATNR AND
           ZMARD-LGORT LT ZCOMB-LGORT
         ).
    " MARD behind COMB
    IF ZMARD-LABST < 0.
       ZMARD-LABST = 0.
    ENDIF.
    WRITE ZMARD-LABST DECIMALS 0 TO ZMARD-ZLABST.
    MODIFY ZMARD INDEX IX_MARD.

    READ_NEXT MARD.

  ELSE.
    " MARD ahead of COMB
    READ_NEXT COMB.

  ENDIF. " Match on material and storage location

ENDWHILE.

WHILE IX_MARD GT 0.
  IF ZMARD-LABST < 0.
     ZMARD-LABST = 0.
  ENDIF.
  WRITE ZMARD-LABST DECIMALS 0 TO ZMARD-ZLABST.
  MODIFY ZMARD INDEX IX_MARD.
  READ_NEXT MARD.
ENDWHILE.

Моля, имайте предвид, че току-що редактирах този код тук, така че очаквам да има някои синтактични грешки и грешки.

person Gert Beukema    schedule 23.06.2015
comment
Благодаря ви много за вашите задълбочени обяснения / оптимизации. Тествах вашите промени и изглежда, че работят добре - но за да избегна твърде много промени в производствената система, добавих само някои where условия в циклите. - person dhh; 24.06.2015

Мисля, че трябва да филтрирате вътрешния цикъл. Освен това, бих дефинирал таблиците като СОРТИРАНИ таблици (С НЕСОРТИРАН КЛЮЧ), поне 'ZCOMB' с ключове MATNR, LGORT и може би IND.

Бих използвал също така променлива sy-tabix, за да запазя позицията на реда и след това да я използвам така:

MODIFY ZMARD INDEX lv_tabix.

Или мога да използвам символ на поле. Това е твое решение.

Кодът би изглеждал така:

DATA lv_index type sy-tabix.

LOOP AT ZMARD.
  lv_index = sy-tabix.
  LOOP AT ZCOMB WHERE MATNR = ZMARD-MATNR AND LGORT = ZMARD-LGORT AND IND = ' '.
             IF ZCOMB-PLUMI = '+'.
                ZMARD-LABST = ZMARD-LABST + ZCOMB-MNG01.
             ELSEIF ZCOMB-PLUMI = '-'.
                ZMARD-LABST = ZMARD-LABST - ZCOMB-MNG01.
             ENDIF.                          "PLUMI
  ENDLOOP.
  IF ZMARD-LABST < 0.
     ZMARD-LABST = 0.
  ENDIF.
  WRITE ZMARD-LABST DECIMALS 0 TO ZMARD-ZLABST.
  MODIFY ZMARD INDEX lv_index.
ENDLOOP.

И накрая, друг подход, по мое мнение, би бил да преминете през „ZCOMB“ и да използвате COLLECT за сумиране на данните и с тези суми да промените „ZMARD“, четейки таблицата с BINARY SEARCH.

Това, по мое мнение, може да подобри производителността, защото ще преминете само върху една таблица.

Дано помогне.

person Nelson Miranda    schedule 23.06.2015
comment
Благодаря ви много за обясненията. Добавих условията where ... към няколко цикъла - но не внедрих другите подобрения, за да избегна твърде много промени в (работещата и тествана) кодова база. - person dhh; 24.06.2015
comment
@dhh Радвам се да помогна. С Най-Добри Пожелания. - person Nelson Miranda; 25.06.2015

Изглежда, че този код иска да актуализира ZMARD-LABST за всички записи в ZMARD. Преминаването на таблица в цикъл на таблица изглежда много бавно за това. Ако сте сортирали ZMARD на MATNR, можете да направите това в 2 различни цикъла. Нещо като

DATA lv_tabix type sy-tabix.
FIELD-SYMBOLS <MARD> like ZMARD. "I dont know the type of ZMARD

SORT ZCOMB BY MATNR.    
LOOP AT ZCOMB.
  IF ZCOMB-IND NE ' '.
    CONTINUE.
  ENDIF.
  IF <MARD>-MATNR NE ZCOMB-MATNR OR <MARD>-LGORT NE ZCOMB-LGORT. 
    "This could be a binary read, because of sorting we only do a read per material
    READ TABLE MARD ASSIGNING <MARD> WITH KEY MATNR = ZCOMB-MATNR LGORT = ZCOMB-LGORT.
    IF SY-SUBRC NE 0.
      CONTINUE.
    ENDIF.
  ENDIF.
  IF ZCOMB-PLUMI = '+'.
    ADD ZCOMB-MNG01 TO <MARD>-LABST.
  ELSEIF ZCOMB-PLUMI = '-'.
    SUBTRACT ZCOMB-MNG01 FROM <MARD>-LABST.
  ENDIF.
ENDLOOP.


LOOP AT ZMARD.
  lv_index = sy-tabix.
  IF ZMARD-LABST < 0.
     ZMARD-LABST = 0.
  ENDIF.
  WRITE ZMARD-LABST DECIMALS 0 TO ZMARD-ZLABST.
  MODIFY ZMARD INDEX lv_tabix.
ENDLOOP.

Имайте предвид, че MARD има 3 ключа: MATNR, BUKRS и LGORT. Надявам се, че или вашите LGORT стойности не пресичат WERKS, или ZCOMB има поле WERKS, което можете да използвате.

person tomdemuyt    schedule 23.06.2015
comment
Вашият код ще излезе с грешка на ‹MARD›, който не е зададен на ред 9. - person Gert Beukema; 23.06.2015
comment
Благодаря ви много за подробните обяснения. За да избегна твърде много промени във вече работещата/тестваната кодова база в производствената система, добавих само условията where в някои цикли. Сега работи много по-бързо. Btw - възможно ли е да се извършват измервания на времето в старата система SAP? - person dhh; 24.06.2015

Ако не ви интересува редът за изпълнение, можете да направите това (нетествано):

SORT ZMARD BY MATNR LGORT.
SORT ZCOMB BY MATNR LGORT.

LOOP AT ZMARD ASSIGNING <ZMARD>.

READ TABLE ZCOMB WITH KEY
                MATNR = <ZMARD>-MATNR
                LGORT = <ZMARD>-LGORT
                TRANSPORTING NO FIELDS.
IF sy-subrc = 0.                            
    lv_index = sy-tabix.
ELSE.
    CONTINUE.
ENDIF.

LOOP AT ZCOMB ASSIGNING <ZCOMB> FROM lv_index.

    IF <ZCOMB>-MATNR <> <ZMARD>-MATNR OR <ZCOMB>-LGORT <> <ZMARD>-LGORT.
        EXIT.
    ENDIF.

    IF <ZCOMB>-IND = ' '.
        IF <ZCOMB>-PLUMI = '+'.
            <ZMARD>-LABST = <ZMARD>-LABST + <ZCOMB>-MNG01.
        ELSEIF ZCOMB-PLUMI = '-'.
            <ZMARD>-LABST = <ZMARD>-LABST - <ZCOMB>-MNG01.
        ENDIF.
    ENDIF.

ENDLOOP. " ZCOMB loop

ENDLOOP. " ZMARD цикъл

person user5056301    schedule 27.06.2015