2

I recently issued an update to a HOST Reporting program. Our shop uses Enterprise PLI.

I added 2 new structures Declared as BASED. So i basically use an ALLOC statement to allocate the required storage for the structures and then pass the pointers to a Fetchable to get some details i need.

It failed with a storage error during a pilot run in production (LEMSG below). It was trying to process more than a million records there and it appears the job basically ran out of storage.

IBM0451S  ONCODE=451  The STORAGE condition was raised.                         
From entry point MXXX at compile unit offset +000001EA at entry offset 

More Details: IBM0451S

As a fix , i am issuing an update to explicitly add a FREE for the storage after the Fetchable call and i also updated the REGION PARM of my JCL to 0M.

Thought i'd check if you have seen this kind of errors before and have any additional thoughts. Thanks.

Here is how my latest updated code looks like

DECLARES
  =======================================
  DCL                                     
   01 IN_DATA  BASED(IN_PTR),       
     % INCLUDE SYSLIB(XXXXXPAA);          
  DCL                                     
   01 OUT_DATA BASED(OUT_PTR),      
     % INCLUDE SYSLIB(YYYYYPAA);          

  DCL                                     
   01 IN_PTR        PTR;                  
  DCL                                     
   01 OUT_PTR       PTR;  

 ======================================

 The below block of code runs for every record that get processed. The    
 FREE statement is what i now added. I was thinking that because i did 
 not have a FREE before , the ALLOC was basically getting new STOARGE
 everytime it executed that block of code and ran out of storage.



 ALLOC IN_DATA;                              
 ALLOC OUT_DATA;                             

 IN_DATA  = '';                              
 OUT_DATA = '';                              

 IN_DATA.CODE =  'XXX';

 CALL FABCD(IN_PTR,                                 
            OUT_PTR);                               

 IF OUT_DATA.RTRN_CD <= 04 THEN               
   DETAIL_REC.XYZ = OUT_DATA.YYY_CODE;   
 ELSE                                               
  ;                                                 


 FREE IN_DATA;    -------->> What i added now                         
 FREE OUT_DATA;   -------->> What i added now                             

=============================================   
cschneid
  • 10,237
  • 1
  • 28
  • 39
HEX
  • 83
  • 1
  • 9
  • Not really, this is generally an application related issue with storage mgmt. Do you have more detail than just the error message? What addressing mode are you running in. Is there a possibility to move to a 64-bit mode ? I'm not a PL/1 expert so others will probably have better advice. – Hogstrom Nov 09 '18 at 19:24
  • We run in AMODE 31 . Running in 64 is not an option. I updated the code snippet below. Thanks. I ran it in test with a reduced test load and it ran OK, but i dont have anywhere close to million records in test. Thanks. – HEX Nov 09 '18 at 20:08
  • It's not uncommon for shops to limit region size with exits (IEFUSI?), so coding REGION=0M may not be giving you what you expect. I don't like giving the old "go talk to your sysprogs" response, but in this case, you really might want to check with them to see if region is being restricted. Other than that, I'm no PL/1 guy and likely can't help much. – Rich Jackson Nov 10 '18 at 10:23

2 Answers2

2

Apart from the storage problem point of view, allocating and freeing storage for each and every record processes is ahuge perforance killer.

From the snipped you show, it is not clear to be a) why you ALLOC in the first place, and b) why you think you need a fresh piece of storage for each record.

Just allocte the structures locally, the pass a pointer to them to the function.

DCL 01 IN_DATA,       
    % INCLUDE SYSLIB(XXXXXPAA);
DCL 01 OUT_DATA,      
    % INCLUDE SYSLIB(YYYYYPAA);          

DCL IN_PTR        PTR INIT ( ADDR( IN_DATA) );                  
DCL OUT_PTR       PTR INIT ( ADDR( OUT_DATA ) );

This will have PL/I allocate the structures only once, but still allow pointers to the storage be passe to the function routine.

I would also remove the second performance killer: The probably unneeded initialization of the structures

IN_DATA  = ‘‘;
OUT_DATA = ‘‘;

This does a field by field initialization. Don‘t do this unless you have good reason.

phunsoft
  • 2,674
  • 1
  • 11
  • 22
  • Need a quick clarification. This module is a sub module called from main module for every record read . So wouldn't the declares get run every time this module is invoked ? Maybe i need to look into adding a first time logic or something wherein i invoke the ALLOC only once the very first time this module is called and then reuse that storage for subsequent calls. Yes the second init is not needed , i will make this update. – HEX Nov 12 '18 at 16:39
  • My bad, I didn’t read too carefully, and issed the point that this code is what is being called for every record. You‘re right, the allocation would be done for each record under the covers. You don‘t show the rest of the logic, so I don‘t know if allocating the structures once one level up,and passing to this code is an option. Otherwise the first time approach you mentioned is surely much, much better than redoing the allocation for each record. There is a slight cost for the repeated first time test. I would try to avoid that as well if possible. – phunsoft Nov 13 '18 at 05:31
0

This is expected behavior. From your description, your initial code had a memory leak, allocating storage without freeing it. Now that you have added code to free the allocated memory when it is no longer needed, you likely don't need the REGION=0M, though as indicated in a comment it may not be doing what you wanted anyway.

cschneid
  • 10,237
  • 1
  • 28
  • 39