ERC32 Ada Technical Summary: For mission-critical applications using the ERC32 spacecraft computer | ||
---|---|---|
Prev | Chapter 5. Performance and Capacity | Next |
The target code performance of ERC32 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 20 MHz clock, zero wait states on data read and write, and one wait state on instruction fetch.
Table 5-1. Benchmark Results
Benchmark | Result at 20 MHz | Percent stalls |
---|---|---|
Ackermann | 637 mSec | 5% |
Sieve | 145 mSec | 27% |
Whetstone | 6410 KWIPS | 27% |
Table 5-2 gives timings for several task-related features. The clock frequency is 20 MHz.
Table 5-2. Task-Related Metrics
Metric | Clock Cycles | Time in Microseconds at 20 MHz |
---|---|---|
Interrupt latency (C.3.1 (15)) | 300 | 15 |
From call of trivial protected procedure to return from entry | 1150 | 58 |
Call of Clock (D.8 (44)) | 13 | 1 |
Lateness of a delay (D.9 (13)) | 1100 | 55 |
Suspend_Until_True, where state is already True | 50 | 2 |
Set_True to return from Suspend_Until_True | 1010 | 50 |
Trivial protected procedure call (D.12 (6)) | 120 | 6 |
ERC32 Ada uses many traditional optimizations to improve the size and execution speed of the generated code. The following list includes some of the optimizations.
Sub-expression commoning
Loop unrolling
Loop variable induction
Strength reduction
Constraint check elimination
Loop invariant hoisting
Load and store elimination
Register allocation
Unreachable code elimination
Tail recursion optimization
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.
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:
A check on the top limit only is performed for (2).
A discriminant check is performed for (4).
No space is allocated for scalar variables that are unused. Space for arrays and records is always allocated.
Subprograms that are declared in a package but unused in a program are always loaded if the package is loaded.
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.
In most cases code that is unreachable is eliminated.
In the following code example, the address of the element of the array is computed once.
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.
In general, redundant array bounds checks are eliminated.
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.
As an example of the subprogram calling overhead, the code sizes for Ackermann's function are as follows:
Total code size for Ackermann's function = 84 bytes
Instructions executed per call = 44. This includes instructions to handle window underflow and overlow on approximately 48 percent of the calls.
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.
For a task 120 bytes are allocated for the task control block. In addition, there are 12 bytes for each task entry and the task's stack. The stack size is either the default size of 4096 bytes, or the value given in the task type's length clause.
The space overhead for a protected object is 25 bytes.
The size of a null program is approximately 5500 bytes. The size of a minimal program that uses tasking (tasks, protected objects and delay statements) is approximately 10K bytes. These sizes include code, read-only data and variables, but exclude stack space.