Coding for speed, size, and portability. « Back

 

Learn how to make your COBOL programs smaller, faster, more portable, and more maintainable also!

Contents

  • Program Structure
  • Data
  • Code
  • Directives

Program Structure

otherwise known as control flow

Your program should be considered as a collection of logically separate procedures. Each procedure should be commented with respect to its input and output and function. There is no trickle through - instead each procedure is invoked using perform.

The section is the procedure, the section name the procedure name. If you like, think of sections as Pascal procedures, or C void functions.

Paragraphs are internal labels that should be regarded as not visible outside the section in which they are used. perform is the function/procedure call.

To achieve this model there are some easy rules.

     
    Do use perform section
    goback at end of first section - must be inline code
    Do NOT use alter
    perform thru
    next sentence
    segments
    go to section-name
    perform paragraph
    go to paragraph outside current section
    You may use go to paragraph within a section.

Programs that follow these guidelines are known as well-behaved, or (well) structured. Well-behaved programs are faster to generate, produce smaller and faster code, and are easier to understand. A win-win-win situation.

It is vitally important to use goback [ returning ... ] or stop run as the last statement of the first section. This makes it is impossible for control to trickle into subsequent sections, which allows the ncg to generate smaller and faster code for performs.

Other miscellaneous control flow guidelines are

goto depending
Fast, efficient, but can be confusing. Ensure all targets are within the section
evaluate
Do not use an expression in the evaluate clause. Use a temporary. when clauses should be ordered so that the most likely cases come first.

Data

Align, align, ALIGN !

On ALL chips, you must align 2 byte data items on 2 byte boundaries, 4 byte items on 4 byte boundaries. Increasingly, it pays to align larger data items on 8 byte boundaries. Misaligned data takes longer to load and store and may require more instructions. When a misaligned item is an operand and the target of arithmetic (ie add a to b) then you get the penalty twice. The situation will only get worse with new chips and new versions of existing chips

     
    Chip Cycles to store pic x(4) comp-5 Additional Instructions for misaligned
    aligned misaligned
    Pentium 1 4
    RS6000, PowerPC 1 2
    MIPS 1 2 1
    HP PA 1 7 6
    Sparc, Alpha 1 10 9
    Itanium 1 4 8

Arrays

An array should have a stride (ie total length of 1 table item) that is a multiple of 4 bytes. This means the alignment of all the subscripted items can be calculated. Ideally the stride is a power of 2.

Arithmetic data

comp is slow on all lohi platforms. This includes all Windows and NT platforms. The bytes need to be swapped. Use comp-5 for speed - comp-x for portable data files. Use 1, 2 or 4 byte items. Use integers.

Alphanumeric

Alphanumeric items should be a multiple of 4 or 8 bytes where possible.

Code

Arithmetic

Try to use unsigned comp-5 items that are the same size. Add and subtracts can have multiple sources, targets and giving clauses (beware of subscripted items though). Add and subtract are always optimised in computes. The optimal length is 4 bytes. Try to avoid conversions to/from display and comp-3 items.

Comparisms against the literal zero are usually the most efficient. Comparisms for (in)equality are usually more efficient than compare for ordering, particularly on lohi machines (ie Pentium).

Multiply is expensive - divide is exorbitant - divide remainder is prohibitive. You have been warned. Multiply by small literal values however is usually reasonable. Multiplication/division by powers of 2 is optimised.

The Cobol rules on the precision of intermediate results are why 2 operand statements are in general better. In particular

    if a + b > c

must be evaluated for 5 bytes (in effect 8) assuming that either a or b is 4 bytes.

    evaluate a + b

is evaluated for every single when clause. Use

    compute c = a + b
    evaluate c

Alphanumerics

Comparisms for (in)equality is faster than comparing for ordering, especially on lohi chips such as Pentium. Alphanumeric items should be multiples of 4 bytes long, and be 4 byte aligned. Variable length moves (ie ref-mod where the length is a variable) are slower than fixed length as they must be done byte by byte. Also - there is no possibility of loop-unrolling and an additional check must be done for 0 length moves.

Calls and files and environment variables

Be consistent in the case you use for calls, filenames and environment variables. Unix and C are case-sensitive. Lower case is the norm for filenames and upper case for environment names. Some modules are in upper case, such as PANELS and FHREDIR. Be sure to use the correct case. Use call literal in preference to call data name. The same applies to

set procedure-pointer to entry.

Do not include extensions or paths - use $COBPATH if files are not in the same directory.

Do not use PC_ calls or call by number - use the appropriate CBL_ routines which also use comp-5. This gives portability and better performance on all platforms.

call on overflow implies that the called program may not be there. Therefore in .o (.obj) a static reference cannot be produced. If an on overflow clause is there only for .int/gnt environments, and you know the modules will be linked then use call convention 8.

Call parameters should be 01 level items, and subprograms generated lnkalign. Do not rely on knowing how many parameters have been passed to subprogram. This is very expensive. If different sub-functions have a different number of parameters, determine this from a sub-function argument.

Entry points and parameters

Parameter processing is most efficient when a program is generated notypecheck nolnkcheck noparamcountcheck. Better still - use nocheck.

Do not use

    if address of <parameter> = NULL

which requires paramcountcheck

If you absolutely have to use this syntax, then any call that does not supply this parameter should pass by value NULL instead.

Do not use by value parameters with length > 4 bytes. This is not portable.

Coding style

This is all subjective of course - but this works for me.

  1. Use all scope-delimiters.
  2. Enforce by
          $ set noimplicitscope change-message(1227 E)
    The ANS85 scope-delimiters are optional. A construct, eg if, nested inside another, ie evaluate, is terminated by end-evaluate or when. This is confusing and potentially buggy. Use the delimiters.

  3. Only use '.' to delimit paragraphs and sections

  4. '.' can be the source of many and subtle and devious bugs. How many times has an extra "." caused a compilation error (if you used scope delimiters) or worse - a run time bug (if you did not). Have you ever added code to the end of the section only to get a compilation error because you forgot the '.' I use
    . process-line section.

    . proc-line-loop.
    to declare sections and paragraphs. This keeps all the '.' on one line, and neatly delineates the section name.

  5. Avoid goto by using

    • in-line performs

    • exit section
    • exit perform
    • exit perform cycle
  6. Replace multiple else if by evaluate true. evaluate true is just as efficient as if ... else if. It is more readable , and your code does not inexorably creep across the page and off the right margin.

  7. align end-verb with verb, and when with evaluate

  8. Use typedefs and call prototypes.

    typedefs and call prototypes are used in almost all languages since ALGOL 60. They enforce good coding style and eliminate a lot of bugs. Enforce adherence to call prototypes by using

    $set change-message(1056 E 1057 E 1058 E 1059 E)
    If you need to override the prototype for some reason (eg the checker sometimes does not accept value pointer as equivalent to reference pointed-to use
    $set change-message(1057 N)
    See also the checker directive PROTOTYPE.

Directives

     
    checker noalter improves checker speed
    noseg improves checker speed
    noqualproc improves checker speed
    catch identical and confusing paragraph names
    nocheck turn off run time checking
    ncg nocheck turn off run time checking
    opt turn on global optimisations
    lnkalign guarantees linkage items are aligned

2 Comments

  • Posted by Alex TurnerPosted on 28-Mar-2011 at 20:06
    Brilliant stuff!

    You missed one thing which I keep hitting. Use the smallest numeric fields you can:

    * binary-long is normally faster than binary-double on 32 bit platforms.
    * pic s9(4)v94) is faster than s9(8)v(8) on 32 bit platforms for a lot of calculations.
    * Anything over 18 digits should be used when required, but is likely to run slower than the alternatives.

    I must create a similar post for COBOL on .Net and JVM.
  • Posted by Bubba Micro Focus StaffPosted on 25-Mar-2011 at 15:01
    Great article Jeremy! I've had multiple people hit me up for information on this very topic. This should be required reading for every developer!
  • 1
Only signed-in users can leave comments.

Article Information

Tags

No tags have been set.

Share