In order for this site to work correctly, and for us to improve the site, we need to store a small file (called a cookie) on your computer.
By continuing to use this website, you agree to our cookies and privacy policy.
Home page Home page Home page Home page
Pixel Header R1 C1 Pixel
Pixel Header R2 C1 Pixel
Pixel Header R3 C1 Pixel

VROOM - Window Processing II

The last issue dealt with the way in which window flow can impair performance, specifically in relation to OCONVs and symbolics. Example code to deal with this problem by removing the OCONVs and symbolics at pre save and restoring them pre read is presented below. Note that the best performance improvements will be seen where the OCONVs and symbolics make reference to other files, eg code table lookups. To use the program, enter compile and catalog it and then put commuter calls to it on the PRE.INIT, PRE.READ and PRE.SAVE hooks. (EG Pre Save Code = S Pre Save Command = STRIPPER,PRE.SAVE. The code listing that follows is longer than our preferred length but this in view of the numerous comments contained therein it is felt to be acceptable.

0002    *
0003    * Author AMcA with acknowledgements to Stefan Gilboy of ICS (Sales) for
0004    *        the original idea
0005    * Date   April 1990
0006    *
0007      BEGIN CASE
0011      END CASE
0012    RETURN
0014    PRE.INIT:
0015      *
0016      * It is necessary to make a copy of all of the symbolics and OCONVs so
0017      * that they can be restored later when they have been removed. Anywhere
0018      * can be used, this example uses REGISTER(2). Note that as this logic
0019      * takes place at PRE.INIT the template structure is still in @RECORD so
0020      * this is manipulated
0021      *
0022      CTR = @RECORD<1> + 1
0023      NEW_REG = ""
0024      FOR X = 2 TO CTR
0025         *
0026         * Note that we store the field name and OCONV for EVERY prompt
0027         * regardless of the relevance of same. It is a moot point whether the
0028         * extra checks required to implement the extra logic might slow the
0029         * program down sufficiently to nullify the benefits accrued therefrom
0030         *
0031         NEW_REG<X-1> = @RECORD<X,2> : @VM : @RECORD<X,13>
0032         *
0033         * Don't null down key prompts as this seems to cause problems under
0034         * some circumstances. For all other fields though remove the field
0035         * name if symbolic to prevent recalculation and the OCONV
0036         *
0037         IF X > 2 THEN
0038            IF @RECORD<X,3> = "S" THEN @RECORD<X,2> = ""
0039            @RECORD<X,13> = ""
0040         END
0041      NEXT
0042      *
0043      * Now store NEW_REG in REGISTER(2)
0044      *
0046    RETURN
0048    AREV.COMMON:
0049      *
0050      * Note - cannot be done at top of program as it is not loaded until
0051      * after PRE.INIT
0052      *
0054    RETURN
0056    PRE.READ:
0058      *
0059      * If @ID is null then this is the "Dummy" pre read highlighted in the
0060      * last issue. Under these circumstances it makes no sense to reload the
0061      * missing information
0062      *
0063      IF @ID # "" THEN
0064         FOR X = 1 TO W.CNT
0065            W(X)<2> = REGISTER(2)<X,1>
0066            W(X)<13> = REGISTER(2)<X,2>
0067         NEXT
0068      END
0069    RETURN
0071    PRE.SAVE:
0073      *
0074      * Remove information
0075      *
0076      FOR X = 1 TO W.CNT
0077         IF W(X)<3> = "S" THEN W(X)<2> = ""
0078         W(X)<13> = ""
0079      NEXT
0080    RETURN

Improving OCONV Performance

As evidenced in the last issue, OCONVs are called an inordinate amount of times in entry windows. If user-defined OCONVs which produce disk i/o are being used then performance will degrade dramatically. It is therefore only practical to examine ways of decreasing this overhead.

One simple but effective improvement that can easily be made is to examine the value that the program is being asked to OCONV and then compare it with the value last OCONVed. If the two values are the same then there is no point in recalculating the value - simply use the result produced last time. The decision of where to store this information is a purely personal one but the higher fields in @RECORD are convenient if you remember to null them down on a Pre Save.

The following logic serves to illustrate this. It assumes the basic structure of the user defined conversion has been defined and the data to convert is in PASSED_DATA whilst the data to return is in RETURNED_DATA.

0001       * Normal OCONV code precedes
0002       IF PASSED_DATA = @RECORD<100,1> THEN
0003          RETURNED_DATA = @RECORD<100,2>
0004       END ELSE
0005          @RECORD<100,1> = PASSED_DATA
0007          @RECORD<100,2> = RETURNED_DATA
0008       END

Note that in the example given above the passed data is stored in value one of the field and the OCONVed result is stored in field 2. This will obviously not work if the field being OCONVed is MVed.

Improving SYMBOLIC performance - RLIST

It is frequently the case that symbolics make reference to other symbolics. As these need to be recalculated every time they are referenced this can reduce list performance. Extrapolating upon the technique used above we could modify our symbolics so that those symbolics that others used could place their result both in @ANS and @RECORD<x>. The other symbolics could reference this field and if null calculate the appropriate value and insert it. This means that in any list statement the same symbolic would only ever be calculated once regardless of how many times it was referenced. This sort of approach is simple to implement - the only problem is remembering to modify new symbolics in line with pre-existing ones.

The performance improvements gained can be significant, in one disk intensive operation at a client recently, list speed was improved by an order of magnitude by applying this technique. The following code illustrates the technique - using two dictionary items, HOURS_TO_DATE and AMOUNT_TO_DATE (assume CALCULATE_HTD and CALCULATE_ATD exist and return the relevant information).

0001    * Hours to date, stored temporarily in @RECORD<100>
0002    IF @RECORD<100> THEN
0003       @ANS = @RECORD<100>
0004    END ELSE
0006       @ANS = HTD
0007       @RECORD<100> = HTD
0008    END
0010    * Amount to date, stored temporarily in @RECORD<101>
0011    IF @RECORD<101> THEN
0012       @ANS = @RECORD<101>
0013    END ELSE
0014       IF @RECORD<100> THEN
0015          HTD = @RECORD<100>
0016       END ELSE
0017          HTD = {HOURS_TO_DATE}
0018          @RECORD<100> = HTD
0019       END
0020       GOSUB
0021       CALCULATE_ATD
0022       @ANS = ATD
0023       @RECORD<101> = ATD
0024    END

(Volume 2, Issue 3, Pages 4-6)
Pixel Footer R1 C1 Pixel