Calling ProgramsNext

Chapter 1: Writing Efficient Programs

The information in this chapter is designed to help you produce programs that run as efficiently as possible. It includes information on the following:

The information in this chapter applies to programs compiled to native code, not intermediate code, unless otherwise stated.

1.1 Performance Programming

This section gives some guidelines which, if followed, enable your Server Express system to optimize fully the native code produced for your programs. This results in smaller and faster applications. Do remember that these are only guidelines; programs that do not conform to these guidelines still run correctly, just less efficiently.

Programs using Server Express can be moved to other systems provided certain portability rules are followed.

1.1.1 Data Types

Using the correct data type is important to get the greatest efficiency from operations, particularly arithmetic operations. For details of how each data type is stored refer to your Language Reference. The following list give information about data types:

1.1.2 Procedure Division Considerations

This section identifies items in the Procedure Division which affect the size and performance of your program, and suggests the most efficient ways of using them.

As a general rule, the simpler the operation, the faster it executes and the smaller the compiled code. To get the best performance it is often better to use a number of simple operations rather than one complex operation. The following are general guidelines that result in the fastest and smallest possible code.

1.1.2.1 Arithmetic Statements

To get the best performance from arithmetic statements always use the simplest forms:

1.1.2.2 Alphanumeric Data Manipulation

The following list suggests the most efficient ways of using alphanumeric data manipulation:

1.1.2.3 Table Handling

The following list suggests the most efficient ways of table handling:

1.1.2.4 Conditional Statements

The following list suggests the most efficient ways of using conditional statements:

1.1.2.5 Logical Operations

A number of COBOL System Library Routines (call-by-name) are available to perform bit-wise logical operations on data items. These are described in the chapter Advanced Language Features. They perform operations such as bit-wise AND, OR and XOR.

The Compiler recognizes calls to these routines and, if possible, optimizes them to produce in-line code rather than calls to the run-time system. The calls are optimized if the length is specified as a literal. In-line code is native code which performs the function directly without making any calls. The alternative is a call to a generic run-time routine which must allow for many cases.

The calls are optimized if the length is specified as a literal.

Logical AND and logical OR operations can also be carried out using the VALUE clause. See your Language Reference for details.

1.1.2.6 The PERFORM Statement

Using PERFORM is generally very efficient, and is a very good way of keeping the size of your program down as well as giving it an easy-to-maintain structure. The following rules enable you to use it in the most efficient ways.

1.1.2.7 CALL Statements

The following list suggests the most efficient ways of using alphanumeric CALL statements:

1.1.2.8 Parameters

Avoid making many references to linkage items. These include items defined in the Linkage Section, items set to CBL_ALLOC_MEM allocated memory, and items defined as EXTERNAL.

Accessing linkage items is always slower than accessing Working-Storage Section items. If a Linkage Section item is used frequently, it is faster to move it into a Working-Storage Section item when the program is entered and move it back to the Linkage Section if necessary before exiting to the calling program. The Working-Storage Section item should then be accessed throughout the program rather than the item in the Linkage Section.

1.1.2.9 Sorting Files

If you use input and output procedures with a file sort in your program it is important that you write them efficiently as they are executed once for each record you are sorting. Inefficient input and output procedures can make the sort process appear to be very slow.

1.1.3 Compiler Directives

A number of Compiler directives can be used to make the native code for a program better optimized. Some of these directives must be used with care; ensure that the behavior you get with them is acceptable.

In general, always use the following directives when compiling your programs to native code:

NOALTER
ALIGN"4" (32-bit systems)
ALIGN"8" (64-bit systems)
COMP
NOBOUND
NOCHECK
NOCHECKDIV
NONESTCALL
NOODOSLIDE
NOQUAL
NOSEG
NOTRUNC

Other suggestions (to help prevent inefficient coding)

REMOVE "ROUNDED"
REMOVE "ERROR"
REMOVE "INITIALIZE"
REMOVE "CORRESPONDING"
REMOVE "THRU"
REMOVE "THROUGH"

By removing these reserved words you prevent the possibility that code using these inefficient constructs will be added to the program.

1.1.3.1 Using Directives to Optimize for Speed

There are many directives you can use to optimize for speed. In some cases, the defaults for Compiler directives are the ones that provide speed optimization. This section points out directives that need to be changed from their default values to provide for speed optimization.

ALTER Directive

For efficiency reasons, you should not use ALTER statements in programs. It is recommended that you avoid them altogether, and compile with NOALTER, to prevent the Compiler from having to produce code to look for them.

BOUND Directive

The BOUND directive does boundary checking on table items.

Your applications can be made faster (and smaller) by compiling with NOBOUND. Otherwise, the Compiler inserts extra code to do boundary checking on all references to table items.

During testing, you should use the BOUND directive until you are satisfied that your program is not referencing data beyond your table limits. For production, NOBOUND gives you the desired efficiency.

BOUNDOPT Directive

The BOUNDOPT directive can be used to optimize your code if the following apply:

When BOUNDOPT is specified, digits in a USAGE DISPLAY subscript above the size of the table are ignored. For example, a PIC 9(3) subscript would be treated as PIC 9(2) for a table with less than 100 entries.

We recommend that you do not use USAGE DISPLAY subscripts.

COMP Directive

The COMP directive prevents code checking for numeric overflow. This produces highly compact and efficient code.

COMP changes the behavior of arithmetic on data items defined as USAGE COMP. It produces more efficient code, but the behavior does not conform to the ANSI standard.

If used with the proper care, COMP can improve the speed of your programs.

NESTCALL Directive

The NESTCALL directive enables nested programs to appear in your program. If you know you have no nested calls in your program, specifying NONESTCALL enables the Compiler to generate slightly more efficient code.

TRUNC Directive

The TRUNC directive causes the Compiler to create code to determine whether USAGE COMP data items need to be truncated or not.

If you are certain that you do not need truncation of USAGE COMP data items, NOTRUNC causes the creation of more efficient code.

1.1.3.2 Using Directives to Optimize for Size

There are many directives you can use to optimize for size. In some cases, the defaults for Compiler directives are the choices that provide size optimization. This section points out directives that need to be changed from their default values to provide for size optimization.

ALIGN Directive

This directive specifies the boundary on which level-01 and level-77 items start. This boundary should always be a power of two, such as two, four or eight. For 32-bit COBOL systems use a minimum of ALIGN"4". Use ALIGN"8" if you are using a 64-bit operating system. Higher powers of two retain efficiency, but increase the amount of unused space between data records.

1.2 Avoiding Data Inaccuracies

You might get unexpected results from arithmetical operations involving floating point calculations, as COBOL does not by default round numbers. For example, say WS02 is defined as a COMP-2 data item, and the following operations are performed:

accept ws02
compute ws02 = ws01
display ws01

If you enter a value of 2.3 when requested by the program, the value displayed will be 2.29.

If you want values to be rounded you must specify the ROUNDED clause. The example above would be rewritten as:

accept ws02
compute ws02 rounded = ws01
display ws01

You can specify that all data-items in a program are rounded or truncated using the Compiler directive FP-ROUNDING. See the chapter Directives for Compiler in your Server Express User's Guide for more details of this directive.

1.3 Implementation of Floating-point on 32-bit and 64-bit COBOL Systems

Server Express provides IEEE floating-point support. The following sections describe the range of values and the accuracy available using floating-point support.

1.3.1 Range

The range of values available with each of the two COBOL binary floating-point data types is as follows:

COMP-1 from 8.43E-37 through 3.37E38
-8.43E-37 through -3.37E38
COMP-2 from 4.19E-307 through 1.67E308
-4.19E-307 through -1.67E308

Note: Although the underlying floating point support used by this COBOL system does support the above ranges, this COBOL system only supports 2 digit exponents.


1.3.2 Accuracy

The following table shows the relationship between storage size and significance.

Type
Size
Significant Digits
COMP-1 4 bytes 6-7
COMP-2 8 bytes 15-16

1.3.3 External Items and Literals

The compiler validates floating point literals to ensure their values are compatible with the mainframe environment. Literal values that do not lie within the following range will cause a compile-time error:

from 0.54 E -78 through 0.72 E +76
and from -0.54 E -78 through -0.72 E +76

1.3.4 Inaccuracies in Floating-point

All IEEE format binary floating-point values consist of:

If the value is non-zero, the exponent has a constant subtracted to give the starting value of the mantissa. Thus, the value of a double precision (COMP-2) item in terms of its mantissa (m) would be:

1*(2**m) + mb0*(2**(m-1)) + mb1*(2**(m-2)) + mb2*(2**(m-3)) + ... + 1*(2**(m-52))

where the mantissa bit (in this example, mb0 through mb51) can be 0 or 1. For COMP-1 (single precision) fields, this becomes an issue when the difference between the maximum and the minimum powers of 2 is greater than 23; for COMP-2 fields, the issue might arise when the difference between the maximum and minimum powers of 2 is greater than 52.

For example, if a COMP-2 field is expressed with one mantissa bit multiplied by 2**56, and another mantissa bit multiplied by 2**2, then the value in the field would need to be approximated, because 56 - 2 = 54, which is greater than 52. This would be true in any floating point implementation on any hardware platform. Additionally, the internal storage format of floating point numbers can differ from operating system to operating system; see your Language Reference for information. These discrepancies can occur because of variations in the internal storage of floating point numbers between operating systems.

Binary floating point can only represent powers of 2 (both positive and negative), and combinations of these powers of 2. For example, a fractional decimal number is represented by adding together the combination of values from the sequence 1/2, 1/4, 1/8, 1/16, 1/32, and so on, that comes closest to the required value. Thus, a value such as 0.625 (i.e., 1/2 + 1/8) can be represented exactly, whereas other values are represented by an approximation (which might be slightly above or slightly below the true value). Similarly, an integer is represented by adding together the combination of values from the sequence 1, 2, 4, 8, 16, and so on, that comes closest to the required value. Thus, a value such as 625 (which can be made up from 1 + 16 + 32 + 64 + 512) can be represented exactly, whereas other values are represented by an approximation.

1.4 Handling Large Programs

The Server Express system enables you to execute statically linked or dynamically loaded code. Statically linked code is embedded within the executable file. Dynamically loadable code is a COBOL callable shared object, or .int or .gnt file that is loaded and run only when the file is called. The code is held in a separate file to the executable file.

When designing a COBOL application program that is to be dynamically loadable, you want it to make efficient use of the available memory. It is possible, using this COBOL system, to create and run programs that use more memory than is physically present in your computer. You can do this by separating the program into smaller programs and using the COBOL call mechanism (see the chapter Calling Programs).


Copyright © 2000 MERANT International Limited. All rights reserved.
This document and the proprietary marks and names used herein are protected by international law.

Calling ProgramsNext