REPORT ZPARPROC.
************************************************************************
* This program implements parallel processing. Many time consuming
* report can be broken into equal pieces and run parallel, reducing
* the runtime (in this case searching for a string in the source code
* of a set of abaps). The main program (mother) creates CHILD_NU
* number of child programs(1-9),gives each of them a separate task, runs
* them parallel and then collects the end-result. The child program is
* actually embedded in the mother program, so the mother virtually
* gives birth of her children. A child that finished it's part, commits
* suicide: deletes itself at the end.
* Here is the result of a performonce test with 1-9 children processes
* for a light and a heavy task:
*
* children#      light(Z* programs)(sec)      heavy(R* programs)(sec) 
* 1              51                           664  100%
* 2              28                           357 
* 3              22                           250 
* 4              20                           232
* 5              18                           207  31%
* 6              19                           209
* 7              18                           219
* 8              21                           202  
* 9              20                           211
*
* Conclusions: 
* The performance starts to deteriorate when the children# exceeds a
*     certain, task specific number (the children start to compete
*     against each other for the same resources)
* The heavier the task - the more significant the performance gain.
************************************************************************
* The parent's data declaration
TABLES: TRDIR, TBTCO, INDX.
PARAMETERS: CHILD_NU TYPE I DEFAULT '3',
            PATTERN(2) DEFAULT 'Z%'.
DATA: C,
      CHILD(32) VALUE 'ZCHILD#',
      JOBNAME(32),
      ABAP_NAME(8).
DATA: BEGIN OF IV_TABLE OCCURS 10,        "Table of intervals
      LOW LIKE TRDIR-NAME,
      HIGH LIKE TRDIR-NAME,
END OF IV_TABLE.
DATA: BEGIN OF ITAB OCCURS 500,           "Table of program names
      LINE(72),
END OF ITAB.
DATA: BEGIN OF CHILD_NAME_TAB OCCURS 10,  "Table of the children's names
      L(7), LL(23),
END OF CHILD_NAME_TAB.
DATA: BEGIN OF RESULT_TAB OCCURS 100,     "The result of a single child
      NAME LIKE TRDIR-NAME,
END OF RESULT_TAB.
DATA: BEGIN OF END_RESULT_TAB OCCURS 100, "The table of the end-results
      NAME LIKE TRDIR-NAME,
END OF END_RESULT_TAB.

* Evenly distribute the work between the children
* With EXEC SQL it would be nicer, but less generic
DATA: INDEX TYPE I.
DATA: LINE_NUM TYPE I.
DATA: INTERVAL TYPE I.
DATA: BEGIN OF RANGE OCCURS 1000,
      NAME LIKE TRDIR-NAME,
END OF RANGE.
* Timestamp
GET TIME. WRITE:/ SY-UZEIT. SKIP.
* Timestamp
SELECT * FROM TRDIR WHERE NAME LIKE PATTERN.    "Fill up the range table
  IF NOT TRDIR-NAME IS INITIAL.
    RANGE = TRDIR-NAME.
    APPEND RANGE.
  ENDIF.
ENDSELECT.
SORT RANGE BY NAME.
DESCRIBE TABLE RANGE LINES LINE_NUM.
INTERVAL = LINE_NUM / CHILD_NU.       "This will be the length of the
                                      "working range of a single child
INDEX = 0.
DO.               "Create the table of distinct ranges: 1 for each child
  INDEX = INDEX + 1.
  READ TABLE RANGE INDEX INDEX.
  IF SY-SUBRC <> 0.
    EXIT.
  ENDIF.
  IV_TABLE-LOW = RANGE-NAME.
  INDEX = INDEX + INTERVAL.
  READ TABLE RANGE INDEX INDEX.
  IF SY-SUBRC <> 0.
    INDEX = LINE_NUM.
    READ TABLE RANGE INDEX INDEX.
    IV_TABLE-HIGH = RANGE-NAME.
    APPEND IV_TABLE.
    EXIT.
  ENDIF.
  IV_TABLE-HIGH = RANGE-NAME.
  APPEND IV_TABLE.
ENDDO.

* Create the children
DATA: NUM.
DO CHILD_NU TIMES.                     "Loop on the children
  NUM = SY-INDEX.                      "The index of the child
  CLEAR ITAB. REFRESH ITAB.
  READ REPORT SY-REPID INTO ITAB.
  LOOP AT ITAB.
    C = ITAB+1(1).
    IF C <> '@'.                     "Give birth of a child:
      DELETE ITAB.                   "Filter it out from mamy's belly
    ELSE.
      SHIFT ITAB LEFT BY 2 PLACES.
      MODIFY ITAB.
    ENDIF.
  ENDLOOP.
*
  LOOP AT ITAB.           "Give the child identity and a task to work on
    REPLACE '#' WITH NUM INTO ITAB.
    READ TABLE IV_TABLE INDEX NUM.
    REPLACE '&' WITH IV_TABLE-LOW INTO ITAB.
    IF SY-SUBRC = 0.MODIFY ITAB.CONTINUE.ENDIF.
    REPLACE '$' WITH IV_TABLE-HIGH INTO ITAB.
    MODIFY ITAB.
  ENDLOOP.
*
  CHILD+6(1) = NUM.      "At last: create the child
  INSERT REPORT CHILD FROM ITAB.
*
  JOBNAME = CHILD.    "Create a distinct job name to run the child in bg
  JOBNAME+8(8) = SY-DATUM.
  JOBNAME+16(8) = SY-UZEIT.
*
  CHILD_NAME_TAB = JOBNAME.   "Save the name of my wonderful child
  APPEND CHILD_NAME_TAB.
*
  PERFORM CREATE_A_CHILD_JOB.  "Start the child
ENDDO.

* Wait for my children to complete their task and get the result
DO.
  LOOP AT CHILD_NAME_TAB.
    SELECT * FROM TBTCO WHERE JOBNAME = CHILD_NAME_TAB.
    ENDSELECT.
    IF TBTCO-STATUS = 'F' OR TBTCO-STATUS = 'A'.
      REFRESH RESULT_TAB.
      IMPORT RESULT_TAB FROM SHARED BUFFER INDX(ST) ID CHILD_NAME_TAB-L.
      LOOP AT RESULT_TAB.         "Merge the results in one single table
        END_RESULT_TAB = RESULT_TAB.
        APPEND END_RESULT_TAB.
      ENDLOOP.
      DELETE CHILD_NAME_TAB.
    ENDIF.
  ENDLOOP.
  IF SY-SUBRC <> 0. EXIT. ENDIF.
ENDDO.

* Hooray, all the children are finished
LOOP AT END_RESULT_TAB.
  WRITE: END_RESULT_TAB.
ENDLOOP.

* Timestamp
GET TIME. SKIP. WRITE:/ SY-UZEIT.
* Timestamp

* Schedule the child to run in background
FORM CREATE_A_CHILD_JOB.
  DATA: JOBCOUNT LIKE TBTCJOB-JOBCOUNT.
  ABAP_NAME = CHILD.
  CALL FUNCTION 'JOB_OPEN'
       EXPORTING
            JOBNAME  = JOBNAME
       IMPORTING
            JOBCOUNT = JOBCOUNT.
  CALL FUNCTION 'JOB_SUBMIT'
       EXPORTING
            JOBNAME   = JOBNAME
            JOBCOUNT  = JOBCOUNT
            AUTHCKNAM = SY-UNAME
            REPORT    = ABAP_NAME.
  CALL FUNCTION 'JOB_CLOSE'
       EXPORTING
            JOBNAME   = JOBNAME
            JOBCOUNT  = JOBCOUNT
            STRTIMMED = 'X'.
ENDFORM.

* Here is the child template in the mothers tummy:

*@program zchild#.
*@tables: trdir, indx.
*@data: begin of itab occurs 500,
*@      line(72),
*@end of itab.
*@data: begin of result_tab occurs 100,
*@      name like trdir-name,
*@end of result_tab.
*@data: string(20) value 'loadi'.
*@
*@select * from trdir where name between
*@'&'
*@and
*@'$'.
*@  read report trdir-name into itab.
*@  loop at itab.
*@    if itab-line cs string.
*@      write: trdir-name.
*@      result_tab = trdir-name.
*@      append result_tab.
*@      exit.
*@    endif.
*@  endloop.
*@endselect.
*@* Export the result to shared buffer
*@export result_tab to shared buffer INDX(ST) ID 'ZCHILD#'.
*@* The child commits a suicide after it has finished it's task
*@delete report 'ZCHILD#'.