An IBM Mainframe Assembler program which reads a very large file was modified to call to a COBOL "stub" program once for each record. The COBOL call is essential, but the program itself does very little work. A slight increase in CPU was naturally expected, but testing shows a very high increase in CPU usage and run time. Why? What can be done to speed it up?
Sample source code sufficient to reproduce the problem:
(1) An Assembler program which calls IEFBR14 8192 times:
SAMPLE CSECT
LR 12,15
USING SAMPLE,12
LHI 9,8192
LOOP CALL IEFBR14
BCT 9,LOOP
SR 15,15
SVC 3
END
Log shows very low resource consumption, reasonable when calling a program which does nothing.
EXCP CPU SRB CLOCK SERV PG
11 .00 .00 .00 603 0
(2) Now code a simple COBOL program which does nothing but GOBACK:
IDENTIFICATION DIVISION.
PROGRAM-ID. COBOL.
PROCEDURE DIVISION. GOBACK.
... and also call it 8192 times from within the same loop:
SAMPLE CSECT
LR 12,15
USING CALLCOB,12
LHI 9,8192
LOOP CALL IEFBR14
CALL COBOL
BCT 9,LOOP
SVC 3
END
Yeow! Now resource consumption is horrible by comparison:
EXCP CPU SRB CLOCK SERV PG
65552 .16 .00 .19 3980K 0
Since at least the 1990's, standard IBM COBOL, PL/I and Fortran have included a built-in feature called "Language Environment" (LE) which provides a standard set of runtime callable services common to all. At runtime, this "environment" is established by initialization routines intended to provide a persistent service across all subsequent interlanguage calls. But while Assembler supports LE, it is not automatically built-in. Thus, when Assembler calls an "LE compliant" language such as COBOL, these initialization routines are invoked with every call, greatly slowing the process. The solution is to have the Assembler "driver" program establish the LE runtime environment, so it persists across all subsequent calls and happens only once. Thankfully, this is easily done by adding a few simple statements to the Assembler source. While few and simple, placement is crucial. Here is some sample "shell" code which can be used as a template:
SHELL CEEENTRY AUTO=DSALEN
* COBOL CALL can be anywhere after CEEENTRY and before CEETERM
CEETERM ,
* declare constants here:
CONSTANT DC CL8'CONSTANT'
PPA CEEPPA , (P)rogram (P)rolog (A)rea
CEEDSA , (D)ynamic (S)torage (A)rea
* declare variables here:
VARIABLE DS CL8
DSALEN EQU *-CEEDSA
CEECAA ,
END
There are other LE-related Assembler macros (see link below), but those above are all that are required for a simple, efficient COBOL call.
After adding the essential statements to the Sample program so as to make it "LE Compliant", but keeping the same IEFBR14/COBOL CALL loop:
SAMPLE CEEENTRY AUTO=DSALEN
LHI 9,8192
LOOP CALL IEFBR14
CALL COBOL
BCT 9,LOOP
CEETERM ,
PPA CEEPPA , (P)ROGRAM (P)ROLOG (A)REA
CEEDSA , (D)YNAMIC (S)TORAGE (A)REA
DSALEN EQU *-CEEDSA
CEECAA ,
END
... now resource consumption is again reasonable for calling an additional program which also does nothing:
EXCP CPU SRB CLOCK SERV PG
23 .00 .00 .00 1814 0
z/OS Language Environment Programming Guide ... Specialized programming tasks ... Assembler considerations ... Assembler macros:
http://www.ibm.com/support/knowledgecenter/en/SSLTBW_2.2.0/com.ibm.zos.v2r2.ceea200/clcasm5.htm
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With