COBOL Module 15 – Modern tooling, performance and safe change
Performance: measuring and writing efficient COBOL
Measure COBOL CPU time the same way every time, then speed up arithmetic, lookups and file handling, with GnuCOBOL timings and the z/OS options that matter.
What you will learn
- Measure a COBOL program's CPU time reproducibly and report the machine, compiler, flags, data and runs
- Choose the USAGE of fields used in arithmetic from measured costs
- Improve a batch program's lookups and file handling while keeping its output identical
- Explain why GnuCOBOL timings do not transfer to z/OS, and which Enterprise COBOL options change speed
Before you start
What you’ll learn · Before you start
What you will learn
- Measure a COBOL program's CPU time reproducibly and report the machine, compiler, flags, data and runs
- Choose the USAGE of fields used in arithmetic from measured costs
- Improve a batch program's lookups and file handling while keeping its output identical
- Explain why GnuCOBOL timings do not transfer to z/OS, and which Enterprise COBOL options change speed
Before you start
On this page
Adding 1.25 to a total 10 million times took 0.836 seconds of CPU with the two fields in USAGE DISPLAY and 0.300 with COMP-5, on the same laptop, for the same total. Numbers like these decide where a change pays for itself, but only if they are measured the same way every time and reported with the machine, the compiler and the data they came from.
This lesson sets up that measurement and applies it to COBOL’s usual costs: the USAGE of numbers in arithmetic, the way a table is searched, and how often a file is opened. Every timing here was recorded with GnuCOBOL 3.2 on one busy machine, which the lesson names, and none of them is a statement about z/OS.
Measure the same way every time, and say how you measured
A timing is evidence only with its conditions. The run report of every measurement on this page names the machine,
an Apple M5 Pro with macOS 26.7.1, the compiler, GnuCOBOL 3.2 with cobc -O2, which passes optimisation to the C
compiler, the data and the number of runs. It reports CPU time: The seconds a processor spent running a program's own instructions, user time for its code and system time for the requests it made to the operating system, such as OPEN and CLOSE. Waiting for a processor or a disk is not counted., the seconds the program
itself kept a processor busy, user and system, and not wall-clock time: The elapsed time of a run from start to end, as a clock on the wall would show it, including every wait for a processor, a disk or another job. On a busy machine it changes far more from run to run than CPU time.: this
machine shared its cores with other work, which stretches elapsed time far more than CPU time.
bench.sh runs each variant once to show its output, then three rounds in which every variant runs once in turn, so a busy moment falls on all of them alike, and prints each run’s CPU seconds with the median:
#!/bin/bash
# Usage: ./bench.sh ROUNDS PROGRAM...: runs each program once to show what it
# prints, then ROUNDS rounds in which every program runs once, in turn, so that a
# busy moment of the machine falls on all of them alike. It prints the CPU
# seconds, user plus system, of every timed run and the median of each program.
set -eu
rounds=$1
shift
for p in "$@"; do
echo "$p: $(./"$p")"
done
TIMEFORMAT='%U %S'
for r in $(seq "$rounds"); do
for p in "$@"; do
{ time "./$p" > /dev/null; } 2>&1 | awk '{ printf "%.3f\n", $1 + $2 }' >> "cpu-$p.txt"
done
done
for p in "$@"; do
runs=$(tr '\n' ' ' < "cpu-$p.txt")
median=$(sort -n "cpu-$p.txt" | sed -n "$(( (rounds + 1) / 2 ))p")
echo "$p: CPU seconds ${runs}median $median"
done The routine around it matters as much as the script. The golden-file tests of the tooling lesson pass before the first measurement and after every change, one change is measured at a time, and a gain smaller than the spread between rounds is not a gain:
The drawing shows the whole scene; its 6 steps are listed below.
Every step as text
- Correct first. A program is only made faster once its golden-file tests pass, so that every later run can be checked against the same output.
- The baseline. bench.sh runs every variant once to show its output, then three rounds in which each variant runs in turn, and keeps the CPU seconds of each run.
- One change. Change one thing, such as the USAGE of two fields. Two changes at once leave you unable to say which one helped, or which one broke something.
- The same output. The golden tests run again. A faster program that writes a different report is a new bug, so the change goes back.
- Measured the same way. The same machine, compiler, flags, data and number of rounds. A gain smaller than the spread between rounds is no gain.
- Record and decide. Keep the change only when every new run beats every old one, and write down the machine, the compiler and its flags, the data and the medians beside the numbers.
The same routine serves every change in this lesson, from the cheapest to try to the ones only a run on z/OS can judge:
| Change | What it means |
|---|---|
| USAGE in arithmetic | DISPLAY, COMP-3, COMP or COMP-5 for the same PICTURE |
| A table lookup | a PERFORM loop over a subscript, SEARCH from the first entry with an index, or SEARCH ALL, a binary search of a table in key order |
| Opening a file | an OPEN and a CLOSE for every record written, or one of each for the whole run |
| OPTIMIZE (z/OS) | how far Enterprise COBOL improves the code it generates |
| ARCH (z/OS) | the IBM Z generation whose instructions the code may use |
| NUMPROC (z/OS) | how the code treats the signs of packed and zoned decimal data |
A lookup inside a read loop and a file opened for every record both repeat once for every record of the input, so their cost grows with the file. The three options change no line of the source, which is why only a timing on z/OS can judge them.
Checkpoint
Check: Measuring CPU time reproducibly
USAGE decides what arithmetic costs, measured on 10 million additions
ADDLOOP adds an amount of PIC S9(3)V99 to a total of PIC S9(15)V99 10,000,000 times. Four builds differ only in the USAGE clause: The clause that says how an item's value is stored: DISPLAY as characters, PACKED-DECIMAL or COMP-3 as packed digits, BINARY, COMP or COMP-5 as binary integers, and COMP-1 or COMP-2 as floating point. More in “USAGE: zoned, packed decimal (COMP-3) and binary” of those two fields, which sed writes in:
IDENTIFICATION DIVISION.
PROGRAM-ID. ADDLOOP.
* Adds 1.25 to a total 10,000,000 times. The USAGE of AMOUNT
* and TOTAL is the only difference between the four variants:
* the session writes it in place of USAGE-HERE with sed.
DATA DIVISION.
WORKING-STORAGE SECTION.
01 AMOUNT PIC S9(3)V99 USAGE USAGE-HERE VALUE 1.25.
01 TOTAL PIC S9(15)V99 USAGE USAGE-HERE VALUE 0.
01 N PIC 9(8) COMP-5.
01 SHOWN PIC Z(14)9.99.
PROCEDURE DIVISION.
PERFORM VARYING N FROM 1 BY 1 UNTIL N > 10000000
ADD AMOUNT TO TOTAL
END-PERFORM
MOVE TOTAL TO SHOWN
DISPLAY "total " FUNCTION TRIM(SHOWN)
STOP RUN. from session import files, run
# The machine and the toolchain, then ADDLOOP built in four USAGE variants with cobc -O2 and timed in three rounds.
# The timings change from run to run; the page reports the recorded ones.
files("addloop.cob", "bench.sh")
run("""
uname -m && sysctl -n machdep.cpu.brand_string && sw_vers -productVersion
cobc --version | head -n 1 && cc --version | head -n 1
for u in DISPLAY COMP-3 COMP COMP-5; do sed "s/USAGE-HERE/$u/" addloop.cob > "add-$u.cob" && cobc -x -O2 -o "add-$u" "add-$u.cob" || exit 1; done
./bench.sh 3 add-DISPLAY add-COMP-3 add-COMP add-COMP-5
""") Output
$ uname -m && sysctl -n machdep.cpu.brand_string && sw_vers -productVersion arm64 Apple M5 Pro 26.7.1 $ cobc --version | head -n 1 && cc --version | head -n 1 cobc (GnuCOBOL) 3.2.0 Apple clang version 21.0.0 (clang-2100.3.34.2) $ for u in DISPLAY COMP-3 COMP COMP-5; do sed "s/USAGE-HERE/$u/" addloop.cob > "add-$u.cob" && cobc -x -O2 -o "add-$u" "add-$u.cob" || exit 1; done $ ./bench.sh 3 add-DISPLAY add-COMP-3 add-COMP add-COMP-5 add-DISPLAY: total 12500000.00 add-COMP-3: total 12500000.00 add-COMP: total 12500000.00 add-COMP-5: total 12500000.00 add-DISPLAY: CPU seconds 0.836 0.837 0.829 median 0.836 add-COMP-3: CPU seconds 0.366 0.369 0.363 median 0.366 add-COMP: CPU seconds 0.321 0.333 0.331 median 0.331 add-COMP-5: CPU seconds 0.300 0.304 0.288 median 0.300
This output changes from run to run: CPU times change from run to run, and the lines that name the machine and the C compiler change with the hardware and its updates
Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 usage_session.py
GnuCOBOL numbers, not z/OS numbers
These timings come from GnuCOBOL 3.2 on one laptop. Enterprise COBOL on z/OS is another compiler on other hardware, so its costs must be measured there, with IBM’s guidance as the starting point.
All four printed the same total, 12,500,000.00, and the medians were far apart:
| USAGE | Median CPU seconds | Against COMP-5 |
|---|---|---|
| DISPLAY | 0.836 | 2.8 times |
| COMP-3 | 0.366 | 1.2 times |
| COMP | 0.331 | 1.1 times |
| COMP-5 | 0.300 | 1 |
The spread inside each row, 0.016 seconds or less, is small next to the gaps between rows, so the order is real. A DISPLAY item holds one digit a byte, as characters, so each ADD works on characters before it can add. COMP-5 is a binary number in the machine’s own byte order. COMP is binary too, but GnuCOBOL keeps it big-endian, as the USAGE lesson showed, and this laptop’s processor is little-endian, which is the likely reason for its small extra cost.
Key idea
Fields that take part in heavy arithmetic belong in a computational USAGE: on this machine DISPLAY cost 2.8 times as much CPU as COMP-5 for the same additions.
Checkpoint
Check: What USAGE costs in arithmetic
Practise: measuring and USAGE
The questions cover the two sections above, and each wrong choice names the slip it comes from.
Practice that adapts to you
Practise this lesson
6 questions about 5 ideas of this lesson. Each answer is explained at once, and what you get right is kept in this browser.
Four lookups timed, from a DISPLAY subscript to SEARCH ALL Pro: opens with a Pro, Premium or Ultimate pass
A lookup inside a read loop runs once for every record, so its cost multiplies. This part times four ways of finding a branch code in a table of 5,000, and puts IBM’s advice on tables beside the numbers.
In this part:
- The cost of one lookup, four ways, measured
- What IBM’s advice on tables adds for z/OS
1 diagram · 2 runnable examples (COB, Python) · about 5 minutes
Opening this part with your pass…
This part opens with any MySmartCoPilot pass.
- Pro pass · 1 week₹79$2.99
- 30 days₹179$5.99
- 1 year₹1,699$45
One payment; a pass never renews. The price shown is the total you pay: no fees or charges are added at checkout.
Passes can’t be bought right now, so this part can’t be opened at the moment. The free part above stands on its own. If you have a pass, enter your licence key.
It also opens every other in-depth part in Learn and every Pro tool. Sales are final, except where the law gives you a right to withdraw, for example in the EU and the UK (refund policy).
Files: one OPEN for all lines, measured, and the output checked unchanged Pro: opens with a Pro, Premium or Ultimate pass
An OPEN and a CLOSE are requests to the operating system, and a program that makes them for every record pays for them every time. This part times both ways of writing 10,000 lines, proves that they write the same file, and has you write the verdict that decides whether a change was worth keeping.
In this part:
- One OPEN for all lines, measured, with the same file written
- The same cost inside a subprogram, and the first-call pattern
- Write the verdict on a change
- Decide from timed runs whether a change made a program faster
- Results of the sample tests
2 runnable examples (COB, Python) · 1 exercise with sample tests · about 5 minutes
Opening this part with your pass…
This part opens with any MySmartCoPilot pass.
- Pro pass · 1 week₹79$2.99
- 30 days₹179$5.99
- 1 year₹1,699$45
One payment; a pass never renews. The price shown is the total you pay: no fees or charges are added at checkout.
Passes can’t be bought right now, so this part can’t be opened at the moment. The free part above stands on its own. If you have a pass, enter your licence key.
It also opens every other in-depth part in Learn and every Pro tool. Sales are final, except where the law gives you a right to withdraw, for example in the EU and the UK (refund policy).
On z/OS: OPTIMIZE, ARCH, NUMPROC and the data types IBM recommends Pro: opens with a Pro, Premium or Ultimate pass
Laptop numbers decide what to try; z/OS numbers decide what to keep. This part gives the Enterprise COBOL 6.5 options that change the speed of the same source, what each one risks, and IBM’s own ranking of the numeric types.
In this part:
- OPTIMIZE, ARCH and NUMPROC: what Enterprise COBOL’s options change
- Why the laptop’s ranking of USAGE does not transfer
about 4 minutes
Opening this part with your pass…
This part opens with any MySmartCoPilot pass.
- Pro pass · 1 week₹79$2.99
- 30 days₹179$5.99
- 1 year₹1,699$45
One payment; a pass never renews. The price shown is the total you pay: no fees or charges are added at checkout.
Passes can’t be bought right now, so this part can’t be opened at the moment. The free part above stands on its own. If you have a pass, enter your licence key.
It also opens every other in-depth part in Learn and every Pro tool. Sales are final, except where the law gives you a right to withdraw, for example in the EU and the UK (refund policy).
Interview questions
The first question is a warm-up with its full answer. The other three each begin with a batch job that has slowed down:
-
Fresher to mid · Warm-up
Why compare the CPU time of two builds of a COBOL program rather than their elapsed time?
CPU time counts the seconds a processor spent on the program itself, user and system, while elapsed time also counts every wait: for a processor that other work holds, for the disk and for other jobs. On a shared machine, or on z/OS beside other workloads, elapsed time changes with whatever else runs, so two builds timed at different moments are not compared fairly. CPU time is steadier, but not perfectly steady, so measure several runs, interleave the builds so a busy spell falls on both, and compare the medians. Report the machine, the compiler and its flags and the data with the numbers. Elapsed time still matters for a batch window, but it is the result of the CPU time and the waits, and each is improved on its own.
-
Mid to senior
A nightly COBOL step has grown from 25 to 40 minutes and now threatens the batch window. How do you find where the time goes before changing any code, and how do you prove that a fix is both faster and still correct?
-
Mid to senior
A colleague proposes changing every COMP-3 field of a large batch program to COMP, because COMP was faster in a laptop test. What do you check before agreeing, on the laptop and on z/OS?
-
Mid to staff
A batch reads 2 million transactions and, for each one, looks up its branch among 5,000 codes with a PERFORM loop over a PIC 9(4) subscript. Estimate the cost of the lookups, and choose between SEARCH, SEARCH ALL, sorting the transactions and a keyed file.
Organisations named on this page
MySmartCoPilot has no connection with IBM. The Enterprise COBOL options and advice here are from IBM’s documentation; every timing was recorded with GnuCOBOL 3.2.
Flashcards · 7 cards
Recall it, then check
Say the answer to yourself before you look: recalling it is what makes it stick.
-
Why time a COBOL program by its CPU time on a shared machine?
CPU time counts only the seconds the program kept a processor busy, while elapsed time stretches with every other job the machine runs.
-
What must go with a timing for it to count as evidence?
The machine, the compiler and its flags, the data and the number of runs, with medians of interleaved rounds compared and the golden-file tests passing before and after.
-
Why run the variants of a benchmark in interleaved rounds?
Each round runs every variant once in turn, so a busy moment on the machine falls on all of them alike rather than on one.
-
What did 10 million additions cost in each USAGE under GnuCOBOL 3.2 on the lesson's laptop?
Median CPU seconds: DISPLAY 0.836, COMP-3 0.366, COMP 0.331, COMP-5 0.300. DISPLAY cost 2.8 times as much as COMP-5.
-
Why is arithmetic on DISPLAY items slower than on COMP-5?
A DISPLAY item keeps one digit a byte as characters, which each ADD must work through before it can add. COMP-5 is a binary number in the machine's own byte order.
-
What do the Enterprise COBOL options OPTIMIZE, ARCH and NUMPROC change?
OPTIMIZE: how far the compiler improves the code it generates. ARCH: which IBM Z generation's instructions it may use. NUMPROC: how it treats the signs of decimal data. Only a timing on z/OS can judge them.
-
When is a measured speed-up not a speed-up?
When it is smaller than the spread between rounds of the same variant. Measure one change at a time and compare medians.
Key takeaways
- A timing counts as evidence only with its machine, compiler, flags, data and runs: CPU seconds of interleaved rounds, compared by their medians, with the golden tests passing before and after.
- On this machine 10 million additions cost 0.836 CPU seconds in DISPLAY and 0.300 in COMP-5, so fields in heavy arithmetic belong in a computational USAGE.
- These are GnuCOBOL numbers on one laptop; z/OS decisions are measured on z/OS.
Terms in this lesson
- CPU time
- The seconds a processor spent running a program's own instructions, user time for its code and system time for the requests it made to the operating system, such as OPEN and CLOSE. Waiting for a processor or a disk is not counted.
- wall-clock time
- The elapsed time of a run from start to end, as a clock on the wall would show it, including every wait for a processor, a disk or another job. On a busy machine it changes far more from run to run than CPU time.
References
- Enterprise COBOL for z/OS 6.5: Performance Tuning Guide (IBM)
- Enterprise COBOL for z/OS 6.5: Choosing efficient computational data items (IBM)
- Enterprise COBOL for z/OS 6.5: Using consistent data types (IBM)
- Enterprise COBOL for z/OS 6.5: Handling tables efficiently (IBM)
- Enterprise COBOL for z/OS 6.5: OPTIMIZE (IBM)
- Enterprise COBOL for z/OS 6.5: ARCH (IBM)
- Enterprise COBOL for z/OS 6.5: NUMPROC (IBM)
- GnuCOBOL 3.2 NEWS (GnuCOBOL project (GNU))
- Bash reference manual: the time reserved word and TIMEFORMAT (Free Software Foundation)
Report a problem with this lesson
Kept only in this browser. Your Learn progress