COBOL

COBOL

COBOL

COBOL is responsible for the efficient, reliable, secure, and unseen day-to-day operations of the world's economy.

 View Only
  • 1.  Interesting optimization in COBOL 5.2

    Posted Tue February 21, 2017 05:17 PM
     identification division.                             program-id. 'sub'                                    data division.                                       working-storage section.                             01  hold-cred-score             pic s9(3) comp-3.        88  valid-cred-score        values 000                                                  300 thru 699                                         998 999.      linkage section.                                     01  parms.                                               05  cred-score              pic 9(3).                                                                 procedure division using parms.                          move cred-score to hold-cred-score               display hold-cred-score                              if valid-cred-score                                      display 'valid credit score'                     else                                                     display 'not a valid credit score'               end-if                                               goback.                                          end program 'sub'.                                 

    When OPT(1) or OPT(2) is specified, the optimizer appears to not check hold-credit-score for the valid values but in fact, using some "interesting logic", appears to check cred-score instead.

    This has caused a problem when cred-score is not a valid zoned decimal.  In particular, it is x'0000F0'.  I'm not really asking how to "fix" this program, as there are probably several ways.  Or even leaving it as-is and fixing the data prior to the calling of this program.  I don't know if this really APARable, since COBOL really makes no guarantee about how it handles invalid data in a zoned decimal (DISPLAY) numeric field.  Just thought I'd "throw it out there" so see if anyone has any comments.

    Related, I know that the ZONECHECK compiler option would catch the invalid data.  Up to this point we have not been using it.  And certainly (?) we don't want to use it for a final production compile.  But I can't decide on a good "philosophy" as to when it should be used.  As we migrate programs to COBOL 5 (or 6, once we start using it) we could use ZONECHECK and do an initial regression test.  After fixing any cases where we are dealing with "bad data" we could stop using it (for that program).  But this kind of assumes that new development won't introduce any new issues with zoned data.  So do we just always do ZONECHECK for "development" compiles, and NOZONECHECK for production?  What kind of performance hit would that be for programs running in dev with ZONECHECK?

    If you are interested, the following is the relevant code generated at OPT(2).

    000036:  003200         move cred-score to hold-cred-score                                                                      000300  4100 8038          000036            LA      R0,56(,R8)            #                                              000038:  003400     display hold-cred-score                                                                                     000304  4110 D0A0          000038            LA      R1,160(,R13)          #  _ArgumentList                                  000308                     000036            SNAPSHOT STMT                                                                   000308  5820 8000          000036            L       R2,0(,R8)             #  BLL_1                                          00030C  F212 8038 2000     000036            PACK    56(2,R8),0(3,R2)      #  HOLD-CRED-SCORE         CRED-SCORE             000312  D100 8039 3001     000036            MVN     57(1,R8),1(R3)        #  HOLD-CRED-SCORE         _$CONSTANT_AREA        000318                     000038            SNAPSHOT STMT                                                                   000318  D217 D0A0 3130     000038            MVC     160(24,R13),304(R3)   #                          _$CONSTANT_AREA+304    00031E  58F0 30D8          000038            L       R15,216(,R3)          #  _ACON                                          000322  5000 D0A8          000038            ST      R0,168(,R13)          #                                                 000326  58C0 D084          000038            L       R12,132(,R13)         #  _@CAA                                          00032A  0DEF               000038            BASR    R14,R15               #  Call "IGZXDSP"                                 00032C  1812               000038            LR      R1,R2                                                                000039:  003500     if valid-cred-score                                                                                         00032E                     000039            SNAPSHOT STMT                                                                   00032E  1700               000036            XR      R0,R0                                                                   000330  BF07 1000          000036            ICM     R0,X'7',0(,R1)        #  CRED-SCORE                                     000334  C00D FF00 00F0     000036            OILF    R0,X'FF0000F0'                                                          00033A  C20D FFF0 F0F0     000039            CFI     R0,X'FFF0F0F0'                                                          000340  A784 0016          000039            JE      L0005                                                                   000344  C20F FFF3 F0F0     000039            CLFI    R0,X'FFF3F0F0'                                                          00034A  A744 0007          000039            JL      L0006                                                                   00034E  C20F FFF6 F9F9     000039            CLFI    R0,X'FFF6F9F9'                                                          000354  A7C4 000C          000039            JNH     L0005                                                                   000358                     000039  L0006:    EQU     *                                                                       000358  D501 8038 3148     000039            CLC     56(2,R8),328(R3)      #  HOLD-CRED-SCORE         _$CONSTANT_AREA+328    00035E  A784 0007          000039            JE      L0005                                                                   000362  D501 8038 314A     000039            CLC     56(2,R8),330(R3)      #  HOLD-CRED-SCORE         _$CONSTANT_AREA+330    000368  A764 000E          000039            JNE     L0007                                                                   00036C                     000039  L0005:    EQU     *                                                                   

    Shoot, this bulletin board really doesn't like COBOL source code.  The line at offset 000330 above is really the following:

    ICM     R0,X'7',0(,R1)        #  CRED-SCORE

    fswarbrick


  • 2.  Re: Interesting optimization in COBOL 5.2

    Posted Tue February 21, 2017 06:22 PM

    It's actually using both fields :-)

    The LINKAGE item is used for the zero, and for the 300 to 699, and the WORKING-STORAGE for the 998 and 999.

    I'm not sure why...

    It would give you a funny case, which you could try, X'0909F8', for instance, for the 9(3) and you should get a match, because for the 998 and the 999 there is the ZAP, so zones discarded.

    For a fix:

           000036            OILF    R0,X'FF0000F0'

    Becomes:

           000036            OILF    R0,X'FFF0F0F0'

    "I don't care what is in the zones, make 'em F".

    Perhaps @Mike Chase df733e85-0838-4de5-bde4-eb0d6ca241cf​ can shed some light on why it goes to the CLC for the last two tests?

    Out of interest, how much does the code change if you make the packed-decimal unsigned?

    BillWoodger


  • 3.  Re: Interesting optimization in COBOL 5.2

    Posted Tue February 21, 2017 06:39 PM

    Ah, you are right.  I didn't notice that.  "even more interesting"...  Smile

    After trimming out the DISPLAY, here is the ASM when hold-credit-score is signed packed-decimal.

       000036:  003200         move cred-score to hold-cred-score                                                                  
       000300  1700               000036            XR      R0,R0                                                               
       000302                     000036            SNAPSHOT STMT                                                               
       000302  5810 8000          000036            L       R1,0(,R8)             #  BLL_1                                      
       000306  F212 8038 1000     000036            PACK    56(2,R8),0(3,R1)      #  HOLD-CRED-SCORE         CRED-SCORE         
       00030C  960F 8039          000036            OI      57(,R8),X'0F'         #  HOLD-CRED-SCORE                            
     

    And here it is when unsigned packed decimal.

       000036:  003200         move cred-score to hold-cred-score                                                                       
       000300  1700               000036            XR      R0,R0                                                                    
       000302                     000036            SNAPSHOT STMT                                                                    
       000302  5810 8000          000036            L       R1,0(,R8)             #  BLL_1                                           
       000306  F212 8038 1000     000036            PACK    56(2,R8),0(3,R1)      #  HOLD-CRED-SCORE         CRED-SCORE              
       00030C  D100 8039 3001     000036            MVN     57(1,R8),1(R3)        #  HOLD-CRED-SCORE         _$CONSTANT_AREA        

    Only the last instruction has changed.
     

    fswarbrick


  • 4.  Re: Interesting optimization in COBOL 5.2

    Posted Wed February 22, 2017 03:35 AM

    Are you sure you have that the correct way around? The OI is going to mean the sign is F, which would be bad in a signed field. The MVN is used in your original post for the signed field, I think I'm safe in assuming to get a C into the sign unconditionally (you can check the value it is using from the CONSTANT AREA on the compile listing, should be the first byte (apparently) and would be X'?C', where the value in ? is irrelevant).

    Talking of the listing, for me, the MVN line should refer to HOLD-CRED-SCORE+1, as it would prior to V5.

    I was going to ask why the packed-decimal was signed, and expect "because signed is always faster". Now you have the evidence that it isn't :-)

    Unfortunately I have no access to V5+, but: if you make the fields five digits long, the OILF can't be used, so what does it do then?; what happens if you change the order of the literals in the 88?; what happens if you separate the 88 into four (or three, or two, or five) individual 88s and use them in one IF with OR for each?

     

    Next I would have suggested removing the DISPLAY, and much-more-than-half expected that the data does not then even get into the WORKING-STORAGE field. After all, it is not used in the program, is it? Well, it is. Only by that last part of the code generated to process the 88. So, can you remove 998 and 999, so the packed-decimal comparison is no longer generated, and see if the data even gets into the WORKING-STORAGE field (by inspection of the code generated).

     

    Note that in your original, the code apparently generated from the MOVE to WORKING-STORAGE isn't where the actual transfer of data is done. Instead, it is in with the DISPLAY code, where the source-field is next needed and where an actual result (output from DISPLAY) is needed. If you change the DISPLAY to another MOVE to an intermediate field, and reference that field after the IF (can be tricky to achieve, perhaps compare it to another LINKAGE SECTION item), the code (with your first example) may shift to within the first IF, and then to the second if you take out the 998 and 999 from the 88.

    I hope I've explained it well enough, and hope you have some time :-)

    And hope that Mike visits.

    BillWoodger


  • 5.  Re: Interesting optimization in COBOL 5.2

    Posted Thu February 23, 2017 07:50 PM

    I did have the assembler "backwards".

    Changing from 3-digit to 5-digit fields (all unsigned) I get something like this:

    000041:  003200     move cred-score to hold-cred-score                                                                      
       0003B4                     000041            SNAPSHOT STMT                                                               
       0003B4  B982 0000          000041            XGR     R0,R0                                                               
       0003B8                     000041            SNAPSHOT POSTCOMPOUND                                                       
       0003B8  5810 8000          000041            L       R1,0(,R8)             #  BLL_1                                      
       0003BC  D204 8038 1000     000041            MVC     56(5,R8),0(R1)        #  HOLD-CRED-SCORE         CRED-SCORE         
       0003C2  96F0 803C          000041            OI      60(,R8),X'F0'         #  HOLD-CRED-SCORE                            
    000042:  003500     if valid-cred-score of hold-cred-score                                                                  
       0003C6                     000042            SNAPSHOT STMT                                                               
       0003C6  EB01 1000 0080     000041            ICMH    R0,X'1',0(,R1)        #  CRED-SCORE                                 
       0003CC  5800 1001          000041            L       R0,1(,R1)             #  CRED-SCORE                                 
       0003D0  C00D 0000 00F0     000041            OILF    R0,X'000000F0'                                                      
       0003D6  C00C FFFF FF00     000041            OIHF    R0,X'FFFFFF00'                                                      
       0003DC  E300 3158 0020     000042            CG      R0,344(,R3)           #  X'FFFFFFF0F0F0F0F0'                        
       0003E2  A784 0016          000042            JE      L0009                                                               
       0003E6  E300 3160 0021     000042            CLG     R0,352(,R3)           #  X'FFFFFFF0F0F3F0F0'                        
       0003EC  A744 0007          000042            JL      L0010                                                               
       0003F0  E300 3168 0021     000042            CLG     R0,360(,R3)           #  X'FFFFFFF0F0F6F9F9'                        
       0003F6  A7C4 000C          000042            JNH     L0009                                                               
       0003FA                     000042  L0010:    EQU     *                                                                   
       0003FA  D504 8038 3148     000042            CLC     56(5,R8),328(R3)      #  HOLD-CRED-SCORE         _$CONSTANT_AREA+328
       000400  A784 0007          000042            JE      L0009                                                               
       000404  D504 8038 314D     000042            CLC     56(5,R8),333(R3)      #  HOLD-CRED-SCORE         _$CONSTANT_AREA+333
       00040A  A764 000E          000042            JNE     L0011                                                               
       00040E                     000042  L0009:    EQU     *                                                                

    I don't have the willpower to do much in the way of additional tests to see what is what.

    fswarbrick


  • 6.  Re: Interesting optimization in COBOL 5.2

    Posted Thu February 23, 2017 07:57 PM

    Curious question, Bill.  Are you working in a shop that is not wanting to upgrade to COBOL 5/6?

    fswarbrick


  • 7.  Re: Interesting optimization in COBOL 5.2

    Posted Fri February 24, 2017 11:06 AM

    Hi all,

     

    I've been reading this all week, but I've been in a 3-day course and Monday was a holiday for us in Toronto, so I'm just getting to this now in a break in our course. Sorry for the delay!

     

    First, ZONECHECK: We haven't measured the performance hit, but checking every zoned sender for validity is definitely going to be slow, and I'd bet it'd be prohibitively slow in production, so we don't recommend it. You're right, a one-time test with ZONECHECK does assume you won't introduce new errors later, or make use of invalid data from other sources.

     

    There are several things interacting here to produce this code. Firstly, there's the order of the comparisons and where they branch. The compiler thinks of code in terms of blocks (a sequence of instructions with only one entrypoint and one exit point), and we can combine those into extended blocks (one entrypoint but multiple exits). Some optimizations work at the extended block level, but not across multiple blocks. The sequence of comparisons that's being handed to the optimizer is such that the MOVE, DISPLAY, and first three comparisons (= 0, < 300, and <= 699) are grouped into an extended block, but the remaining compares aren't. Within the extended block, we optimize to use the source data as much as possible, for packed and zoned, because if we wait for the result to be written and then read that memory, rather than reading the already-written source, we can end up with a lot of read-after-write dependencies which are awful for performance. (Incidentally, that's a key reason why we try to use decimal floating point arithmetic instead of packed arithmetic as much as possible). Outside the extended block, though, the remaining comparisons can't guarantee, without a lot of extra work in the general case, that it's safe to use the source values, so we *have* to read from the destination, and in doing so, the compiler chooses the best instructions, CLC, for the situation.

     

    Bill's right that modifying the OILF to use FFF0F0F0 is a better choice, and in fact, that's what we do if you use ZONEDATA(NOPFD). You're using ZONEDATA(PFD), I assume, which leads the compiler to believe that your data is accurate. If you use ZONEDATA(MIG) (best if you used NUMPROC(MIG) in V4) or ZONEDATA(NOPFD) (if you used NUMPROC(NOPFD) or NUMPROC(PFD) in V4), then you'll get a guarantee that all comparisons will behave the way they did in V4 with the respective NUMPROC option. This is true however we choose to do the comparison, whether with a CP or CLC, or using a CLFI or other register-based instruction as we do here, or if you're compiling at ARCH(10) or higher and some packed arithmetic is involved and the compiler converts to DFP.

     

    ZONEDATA(PFD): The compiler makes the most efficient choice, and your results will be consistent with V4 because you're guaranteeing your zoned data is all valid (including that all zone bits are x'F')
    ZONEDATA(MIG): The compiler always does zoned compares by converting to packed first, getting rid of zone bits, just like NUMPROC(MIG) in V4 did
    ZONEDATA(NOPFD): The compiler makes comparisons that consider (CLC or something equivalent), or don't consider (PACK/CP or something else that gets rid of zone bits), zone bits in exactly the situations where V4 considered or didn't consider zone bits.

    Mike Chase


  • 8.  Re: Interesting optimization in COBOL 5.2

    Posted Fri February 24, 2017 06:54 PM

    I was not aware of the ZONEDATA options.  Since we are coming from NUMPROC(MIG) it looks like the one we should be using.  That's good to know!

    fswarbrick


  • 9.  Re: Interesting optimization in COBOL 5.2

    Posted Mon February 27, 2017 03:22 AM

    Thanks Mike. Very interesting.

    So a block has one entry, one exit. Taking a typical IF which contains an AND or an OR, there will be multiple blocks (at least two) for the code generated.

    Multiple blocks can be included in an extended block (presumably there are some limits),

    Not all optimisations can be across blocks which are not in an extended block, across extended blocks, or accross a mixture of extended blocks and blocks.

    Unit 1

    Unit 2

    Unit 3

    Whether each unit is a block or an extended block, there are some optimisations which can't be done across those units (but which can be done within the units).

    An extended block may not be able to contain all the processing for one condition. Even with a very simple (to humans) IF like Frank's example, there is quite a lot going on, and that may not fit in an extended block. Where it does not fit in an extended block, an optimisation that was applied to the extended block may not be able to be applied to the subsequent block (here, because it can't be known, easily, in the block for the remainder of the condition that it is still safe to use the source field rather than the destination.

    Is that about right?

    You say that the CLC is the best choice at the time (over a CP for instance), but how was the OILF and friends a good choice at first (better than the CLC) and later not such a good choice?

    And what about the listing, where the reference to the sign-byte only (either the OI or the MVN) are labelled as using the whole field. Does the optimiser not know where the entire field starts, so it has to work only with what it knows?

    BillWoodger


  • 10.  Re: Interesting optimization in COBOL 5.2

    Posted Mon February 27, 2017 10:15 AM

    Hi Bill,

     

    Yes, you have the analysis correct.

     

    OILF was the best choice in the first case because we were comparing unsigned zoned data. Since the sign codes would be x'F' in both cases, we only really need to compare the digits. In the second case, though, we're comparing signed packed data with NUMPROC(NOPFD), so we can't use a CLC, because numerically-equal values with signs of x'A', x'C', orx'E', or signs of x'B' or x'D', need to compare as equal. If we were using NUMPROC(PFD), we would generate a CLC, since there's then the guarantee that there's only one sign code used for positive values and one for negative values.

     

    As for the last part, we have the knowledge to put the offsets within the fields; for some reason, we just aren't. It hasn't seemed to be a pressing thing to do at any point.

    Mike Chase