5.2. Target Code Performance

The target code performance of M1750 Ada is generally very good. The compiler generates code that compares well with other compilers, and which the assembly language programmer would find difficult to beat. See the examples of generated code in Appendix A.

The results of running the three benchmark programs Sieve, Ackermann and Whetstone are given in Table 5-1. These programs were run on the simulator, with a 10 MHz generic 1750A. [1]

Table 5-1. Benchmark Results

BenchmarkBasic memoryExpanded memory
Ackermann948 mSec1913 mSec
Sieve483 mSec483 mSec
Whetstone2862 KWIPS2064 KWIPS

Table 5-2 gives timings for several task-related features. The clock frequency is 10 MHz.

Table 5-2. Task-Related Metrics

MetricClock CyclesTime in Microseconds at 10 MHz
Interrupt latency (C.3.1 (15)) 1500 150
From call of trivial protected procedure to return from entry 1500 150
Call of Clock (D.8 (44)) 170 17
Lateness of a delay (D.9 (13)) 2000 200
Suspend_Until_True, where state is already True 800 80
Set_True to return from Suspend_Until_True 1900 190
Trivial protected procedure call (D.12 (6)) 820 82

5.2.1. Optimization and Code Quality

M1750 Ada uses many traditional optimizations to improve the size and execution speed of the generated code. The following list includes some of the optimizations.

The overall level of optimization is controlled by the -O option. The default is optimization level 2. Also many of the optimizations are tied to a further compile-time option and can be enabled or disabled as necessary.

5.2.2. Constraint Checks

In general, constraint checks are eliminated wherever possible, and constraint check expressions are subject to all the usual optimizations.

Most redundant checks are eliminated. In the example that follows, constraint checks such as those at (1), (2) and (3) are generally eliminated.


I : Integer range -2 .. 2;
J : Integer range 0 .. 10;

type BT is access T;
V : BT;

I := 22 mod 3;    -- (1) no checks needed at run time
I := J;           -- (2) check on top limit only
V := new T (...);
if V.L = ... then -- (3) no null access check
                  -- (4) current variant is correct

In the example shown, the run-time checks performed are as follows:

5.2.3. Space for Unused Variables

No space is allocated for scalar variables that are unused. Space for arrays and records is always allocated.

5.2.4. Space for Unused Subprograms

Subprograms that are declared in a package but unused in a program are always loaded if the package is loaded.

5.2.5. Evaluation of Static Expressions

Static expressions are always evaluated according to the rules of the Ada 95 Reference Manual Section 4.8. Other compile-time-constant expressions may be evaluated at compile time too.

5.2.6. Elimination of Unreachable Code

In most cases code that is unreachable is eliminated.

5.2.7. Common Sub-expressions

In the following code example, the address of the element of the array is computed once.


A(I) := A(I) + 1;

5.2.8. Loop Invariants

In the following Matrix code, the address of the element A(I, J) is computed for the first iteration, then for subsequent iterations the address is incremented by the size of the element.


for I in 1 .. N loop
   for J in 1 .. M loop
      A (I, J) ...
   end loop;
end loop;

5.2.9. Bound Checks

In general, redundant array bounds checks are eliminated.

5.2.10. The pragma Inline

The pragma Inline is supported, except where the subroutine mentioned in the pragma is ineligible. Inlining across compilation units may be disabled using a compile-time option.

5.2.11. Procedure Calling Overhead

As an example of the subprogram calling overhead, the code sizes for Ackermann's function are as follows:

Stack overflow checking adds 7 instructions to the size of the generated code.

5.2.12. The Rendezvous

In a rendezvous, the accept statement body is executed by the owning task, never by the calling task. No tasking optimizations are performed but the special case of a null accept statement is handled separately.

5.2.13. Space Requirements

For a task 74 bytes are allocated for the task control block. In addition, there are 6 bytes for each task entry. The stack size is either the default size of 1024 bytes, or the value given in the task type's length clause.

The space overhead for a protected object is 14 bytes.

The size of a null program is approximately 1548 bytes. The size of a minimal program that uses tasking (tasks, protected objects and delay statements) is approximately 4K bytes. These sizes include code, read-only data and variables, but exclude stack space.

Notes

[1]

The generic 1750A runs with one clock cycle per instruction plus one clock cycle per memory access.