Skip to main content

10.6 Types

Aggregates for Type Extensions

guideline

  • Use only simple aggregates when measured performance indicates.

example

   type Parent is tagged
record
C1 : Float;
C2 : Float;
end record;

type Extension is new Parent with
record
C3 : Float;
C4 : Float;
end record;

Parent_Var : Parent := (C1 => Float_Var1, C2 => Float_Var2);
Exten_Var : Extension;
...
-- Simple aggregate
-- (See ACES V2.0, test "a9_ob_simp_aggregate_02")
Exten_Var := (C1 => Float_Var1, C2 => Float_Var2,
C3 => Float_Var3, C4 => Float_Var4);
-- Extension aggregate
-- (See ACES V2.0, test "a9_ob_ext_aggregate_02")
Exten_Var := (Parent_Var with C3 => Float_Var3, C4 => Float_Var4);

rationale

Determine the impact of using extension aggregates. There may be a significant performance difference between evaluation of simple aggregates and evaluation of extension aggregates.

Protected Types

guideline

  • For mutual exclusion, when measured performance indicates, use protected types as an alternative to tasking rendezvous.
  • To implement an interrupt handler, when performance measurement indicates, use a protected procedure.

example

   -- (1) Using protected objects
-- (See ACES V2.0, test "a9_pt_prot_access_02")
protected Object is
function Read return Float;
procedure Write (Value : in Float);
private
Data : Float;
end Object;
protected body Object is
function Read return Float is
begin
return Data;
end Read;
procedure Write (Value : in Float) is
begin
Data := Value;
end Write;
end Object;
task type Modify is
end Modify;
type Mod_Bunch is array (1 .. 5) of Modify;
task body Modify is
...
begin -- Modify
for I in 1 .. 200 loop
The_Value := Object.Read;
Object.Write (The_Value - 0.125);
if The_Value < -1.0E7 then
The_Value := 1.0;
end if;
end loop;
end Modify;
...
-- Block statement to be timed
declare
Contending_Tasks : array (1 .. 5) of Modify;
begin
null; -- 5 tasks contend for access to protected data
end;
------------------------------------------------------------------------------
-- (2) Using monitor task
-- (See ACES V2.0, test "tk_rz_entry_access_02")
Task Object is
entry Write (Value : in Float);
entry Read (Value : out Float);
end Object;
task body Object is
Data : Float;
begin -- Object
loop
select
accept Write (Value : in Float) do
Data := Value;
end Write;
or
accept Read (Value : out Float) do
Value := Data;
end Read;
or
terminate;
end select;
end loop;
end Object;
-- Task type Modify declared as above
-- Block statement to be timed as above

rationale

Protected objects are meant to be much faster than tasks used for the same purpose (see Guideline 6.1.1). Determine the impact of using protected objects to provide access safely to encapsulated data in a concurrent program.

Bit Operations on Modular Types

guideline

  • Use modular types rather than packed Boolean arrays when measured performance indicates.

example

   -- (1) Packed Boolean arrays
-- (See ACES V2.0, test "dr_ba_bool_arrays_11")

type Set is array (0 .. 15) of Boolean;
pragma Pack (Set);

S1 : Set;
S2 : Set;
Empty : Set := (Set'Range => False);
Result : Boolean;

...

-- Is S1 a subset of S2?
Result := ((S1 and not S2) = Empty);

---------------------------------------------------------------------

-- (2) Modular types
-- (See ACES V2.0, test "a9_ms_modular_oper_02")

type Set is mod 16;

S1 : Set;
S2 : Set;
Empty : Set := 0;
Result : Boolean;

...

-- Is S1 a subset of S2?
Result := ((S1 and not S2) = Empty);

rationale

Determine the impact of performing bit-wise operations on modular types. The performance of these operations may be significantly different from similar operations on packed Boolean arrays. See also Guideline 10.5.7.

Bounded Strings

guideline

  • Use the predefined bounded strings when predictable performance is an issue and measured performance indicates.

rationale

The unbounded strings may be allocated on the heap. If bounded strings are not allocated on the heap, then they may provide better performance. Determine the impact of using the string type declared in instantiations of Ada.Strings.Bounded.Generic_Bounded_Length versus the type declared in Ada.Strings.Unbounded.

The predefined Ada 95 language environment defines packages that support both bounded and unbounded strings. Using bounded strings may avoid the unpredictable duration of delays associated with using heap storage.

String Handling Subprograms

guideline

  • Use the procedural form of the string handling subprograms when measured performance indicates.

rationale

Determine the relative performance cost of functions and procedures having the same name and functionality in Ada.Strings.Fixed, Ada.Strings.Bounded, Ada.Strings.Unbounded and the corresponding child packages whose names include Wide.

While functional notation typically leads to clearer code, it may cause the compiler to generate additional copying operations.

Constraint Checking

guideline

  • Use strong typing with carefully selected constraints to reduce run-time constraint checking when measured performance indicates.

example

In this example, two potential constraint checks are eliminated. If the function Get_Response returns String, then the initialization of the variable Input would require constraint checking. If the variable Last is type Positive, then the assignment inside the loop would require constraint checking:

   ...
subtype Name_Index is Positive range 1 .. 32;
subtype Name is String (Name_Index);
...
function Get_Response return Name is separate;
...
begin
...
Find_Last_Period:
declare
-- No Constraint Checking needed for initialization
Input : constant Name := Get_Response;
Last_Period : Name_Index := 1;
begin -- Find_Last_Period
for I in Input'Range loop
if Input(I) = '.' then
-- No Constraint Checking needed in this `tight' loop
Last_Period := I;
end if;
end loop;
...
end Find_Last_Period;

rationale

Because run-time constraint checking is associated with slow performance, it is not intuitive that the addition of constrained subtypes could actually improve performance. However, the need for constraint checking appears in many places regardless of the use of constrained subtypes. Even assignments to variables that use the predefined subtypes may need constraint checks. By consistently using constrained subtypes, many of the unnecessary run-time checking can be eliminated. Instead, the checking is usually moved to less frequently executed code involved in system input. In the example, the function Get_Response may need to check the length of a user-supplied string and raise an exception.

Some compilers can do additional optimizations based on the information provided by constrained subtypes. For example, although an unconstrained array does not have a fixed size, it has a maximum size that can be determined from the range of its index. Performance can be improved by limiting this maximum size to a "reasonable" number. Refer to the discussion on unconstrained arrays found in NASA (1992).

Real-Time System Annex

guideline

  • For cases where both rendezvous and protected types are inefficient, consider the use of the Real-Time Systems Annex (Ada Reference Manual 1995, Annex D).

rationale

The packages Ada.Synchronous_Task_Control and Ada.Asynchronous_Task_Control have been defined to provide an alternative to tasking and protected types for use in applications where a minimal run-time is desired (Ada Reference Manual 1995, Annex D).