Skip to main content

D.14 Execution Time

danger

This Reference Manual output has not been verified, and may contain omissions or errors. Report any problems on the tracking issue

1/3

This subclause describes a language-defined package to measure execution time.

Static Semantics

2/2

The following language-defined library package exists:

3/5

with Ada.Task_Identification; with Ada.Real_Time; use Ada.Real_Time; package Ada.Execution_Time with Nonblocking, Global => in out synchronized is 4/2 type CPU_Time is private; CPU_Time_First : constant CPU_Time; CPU_Time_Last : constant CPU_Time; CPU_Time_Unit : constant := implementation-defined-real-number; CPU_Tick : constant Time_Span; 5/2 function Clock (T : Ada.Task_Identification.Task_Id := Ada.Task_Identification.Current_Task) return CPU_Time; 6/2 function "+" (Left : CPU_Time; Right : Time_Span) return CPU_Time; function "+" (Left : Time_Span; Right : CPU_Time) return CPU_Time; function "-" (Left : CPU_Time; Right : Time_Span) return CPU_Time; function "-" (Left : CPU_Time; Right : CPU_Time) return Time_Span; 7/2 function "<" (Left, Right : CPU_Time) return Boolean; function "<=" (Left, Right : CPU_Time) return Boolean; function ">" (Left, Right : CPU_Time) return Boolean; function ">=" (Left, Right : CPU_Time) return Boolean; 8/2 procedure Split (T : in CPU_Time; SC : out Seconds_Count; TS : out Time_Span); 9/2 function Time_Of (SC : Seconds_Count; TS : Time_Span := Time_Span_Zero) return CPU_Time; 9.1/3

Interrupt_Clocks_Supported : constant Boolean := implementation-defined; 9.2/3

Separate_Interrupt_Clocks_Supported : constant Boolean := implementation-defined; 9.3/3

function Clock_For_Interrupts return CPU_Time; 10/2 private ... -- not specified by the language end Ada.Execution_Time;

11/3

The execution time or CPU time of a given task is defined as the time spent by the system executing that task, including the time spent executing run-time or system services on its behalf. The mechanism used to measure execution time is implementation defined. The Boolean constant Interrupt_Clocks_Supported is set to True if the implementation separately accounts for the execution time of interrupt handlers. If it is set to False it is implementation defined which task, if any, is charged the execution time that is consumed by interrupt handlers. The Boolean constant Separate_Interrupt_Clocks_Supported is set to True if the implementation separately accounts for the execution time of individual interrupt handlers (see D.14.3).

11.a/2
discussion

The implementation-defined properties above and of the values declared in the package are repeated in Documentation Requirements, so we don't mark them as implementation-defined.

12/2

The type CPU_Time represents the execution time of a task. The set of values of this type corresponds one-to-one with an implementation-defined range of mathematical integers.

13/2

The CPU_Time value I represents the half-open execution-time interval that starts with I*CPU_Time_Unit and is limited by (I+1)*CPU_Time_Unit, where CPU_Time_Unit is an implementation-defined real number. For each task, the execution time value is set to zero at the creation of the task.

13.a/2
ramification

Since it is implementation-defined which task is charged execution time for system services, the execution time value may become nonzero even before the start of the activation of the task.

14/2

CPU_Time_First and CPU_Time_Last are the smallest and largest values of the CPU_Time type, respectively.

14.1/3

The execution time value for the function Clock_For_Interrupts is initialized to zero.

Dynamic Semantics

15/2

CPU_Time_Unit is the smallest amount of execution time representable by the CPU_Time type; it is expressed in seconds. A CPU clock tick is an execution time interval during which the clock value (as observed by calling the Clock function) remains constant. CPU_Tick is the average length of such intervals.

16/2

The effects of the operators on CPU_Time and Time_Span are as for the operators defined for integer types.

17/2

The function Clock returns the current execution time of the task identified by T; Tasking_Error is raised if that task has terminated; Program_Error is raised if the value of T is Task_Identification.Null_Task_Id.

18/2

The effects of the Split and Time_Of operations are defined as follows, treating values of type CPU_Time, Time_Span, and Seconds_Count as mathematical integers. The effect of Split (T, SC, TS) is to set SC and TS to values such that T*CPU_Time_Unit = SC*1.0 + TS*CPU_Time_Unit, and 0.0 <= TS*CPU_Time_Unit < 1.0. The value returned by Time_Of(SC,TS) is the execution-time value T such that T*CPU_Time_Unit=SC*1.0 + TS*CPU_Time_Unit.

18.1/3

The function Clock_For_Interrupts returns the total cumulative time spent executing within all interrupt handlers. This time is not allocated to any task execution time clock. If Interrupt_Clocks_Supported is set to False the function raises Program_Error.

Erroneous Execution

19/2

For a call of Clock, if the task identified by T no longer exists, the execution of the program is erroneous.

Implementation Requirements

20/2

The range of CPU_Time values shall be sufficient to uniquely represent the range of execution times from the task start-up to 50 years of execution time later. CPU_Tick shall be no greater than 1 millisecond.

Documentation Requirements

21/2

The implementation shall document the values of CPU_Time_First, CPU_Time_Last, CPU_Time_Unit, and CPU_Tick.

21.a/2

Documentation Requirement: The values of CPU_Time_First, CPU_Time_Last, CPU_Time_Unit, and CPU_Tick of package Execution_Time.

22/2

The implementation shall document the properties of the underlying mechanism used to measure execution times, such as the range of values supported and any relevant aspects of the underlying hardware or operating system facilities used.

22.a/3

Documentation Requirement: The properties of the mechanism used to implement package Execution_Time, including the values of the constants defined in the package.

Metrics

23/2

The implementation shall document the following metrics:

24/2
  • An upper bound on the execution-time duration of a clock tick. This is a value D such that if t1 and t2 are any execution times of a given task such that t1 < t2 and Clockt1 = Clockt2 then t2 – t1 <= D.
  • 25/2
  • An upper bound on the size of a clock jump. A clock jump is the difference between two successive distinct values of an execution-time clock (as observed by calling the Clock function with the same Task_Id).
  • 26/2
  • An upper bound on the execution time of a call to the Clock function, in processor clock cycles.
  • 27/2
  • Upper bounds on the execution times of the operators of the type CPU_Time, in processor clock cycles.
27.a/2

Documentation Requirement: The metrics for execution time.

Implementation Permissions

28/5

Implementations targeted to machines with word size smaller than 32 bits may omit support for the full range and granularity of the CPU_Time type.

Implementation Advice

29/2

When appropriate, implementations should provide configuration mechanisms to change the value of CPU_Tick.

29.a/2
implementation advice

When appropriate, implementations should provide configuration mechanisms to change the value of Execution_Time.CPU_Tick.

Extensions to Ada 95

29.b/2

The package Execution_Time is new.

Incompatibilities With Ada 2005

29.c/3

Function Clock_For_Interrupts, and constants Interrupt_Clocks_Supported and Separate_Interrupt_Clocks_Supported are added to Execution_Time. If Execution_Time is referenced in a use_clause, and an entity E with a defining_identifier of one of the added entities is defined in a package that is also referenced in a use_clause, the entity E may no longer be use-visible, resulting in errors. This should be rare and is easily fixed if it does occur.

Wording Changes from Ada 2005

29.d/3

If Interrupt_Clocks_Supported is True, it is now possible to determine the execution time of interrupt handlers. This is not an inconsistency, as not charging any task for such time was a legitimate implementation for Ada 2005.

D.14.1 Execution Time Timers

1/3

This subclause describes a language-defined package that provides a facility for calling a handler when a task has used a defined amount of CPU time.

Static Semantics

2/2

The following language-defined library package exists:

3/5

with System; package Ada.Execution_Time.Timers with Nonblocking, Global => in out synchronized is 4/2 type Timer (T : not null access constant Ada.Task_Identification.Task_Id) is tagged limited private; 5/5

type Timer_Handler is access protected procedure (TM : in out Timer) with Nonblocking => False; 6/2 Min_Handler_Ceiling : constant System.Any_Priority := implementation-defined; 7/2 procedure Set_Handler (TM : in out Timer; In_Time : in Time_Span; Handler : in Timer_Handler); procedure Set_Handler (TM : in out Timer; At_Time : in CPU_Time; Handler : in Timer_Handler); function Current_Handler (TM : Timer) return Timer_Handler; procedure Cancel_Handler (TM : in out Timer; Cancelled : out Boolean); 8/2 function Time_Remaining (TM : Timer) return Time_Span; 9/2 Timer_Resource_Error : exception; 10/2 private ... -- not specified by the language end Ada.Execution_Time.Timers;

11/2

The type Timer represents an execution-time event for a single task and is capable of detecting execution-time overruns. The access discriminant T identifies the task concerned. The type Timer needs finalization (see 7.6).

12/2

An object of type Timer is said to be set if it is associated with a nonnull value of type Timer_Handler and cleared otherwise. All Timer objects are initially cleared.

13/2

The type Timer_Handler identifies a protected procedure to be executed by the implementation when the timer expires. Such a protected procedure is called a handler.

13.a/2
discussion

Type Timer is tagged. This makes it possible to share a handler between several events. In simple cases, 'Access can be used to compare the parameter with a specific timer object (this works because a tagged type is a by-reference type). In more complex cases, a type extension of type Timer can be declared; a double type conversion can be used to access the extension data. An example of how this can be done can be found for the similar type Timing_Event, see D.15.

Dynamic Semantics

14/2

When a Timer object is created, or upon the first call of a Set_Handler procedure with the timer as parameter, the resources required to operate an execution-time timer based on the associated execution-time clock are allocated and initialized. If this operation would exceed the available resources, Timer_Resource_Error is raised.

15/3

The procedures Set_Handler associate the handler Handler with the timer TM: if Handler is null, the timer is cleared; otherwise, it is set. The first procedure Set_Handler loads the timer TM with an interval specified by the Time_Span parameter. In this mode, the timer TM expires when the execution time of the task identified by TM.T.all has increased by In_Time; if In_Time is less than or equal to zero, the timer expires immediately. The second procedure Set_Handler loads the timer TM with the absolute value specified by At_Time. In this mode, the timer TM expires when the execution time of the task identified by TM.T.all reaches At_Time; if the value of At_Time has already been reached when Set_Handler is called, the timer expires immediately.

15.a/2
implementation note

Since an access-to-constant can designate a variable, the Task_Id value designated by the discriminant of a Timer object can be changed after the object is created. Thus, an implementation cannot use the value of the Task_Id other than where this Reference Manual specifies. For instance, the Task_Id should be read when the timer is set, but it should not be used when the timer expires (as it may designate a different task at that point).

16/2

A call of a procedure Set_Handler for a timer that is already set replaces the handler and the (absolute or relative) execution time; if Handler is not null, the timer remains set.

17/2

When a timer expires, the associated handler is executed, passing the timer as parameter. The initial action of the execution of the handler is to clear the event.

18/3

The function Current_Handler returns the handler associated with the timer TM if that timer is set; otherwise, it returns null.

19/3

The procedure Cancel_Handler clears the timer if it is set. Cancelled is assigned True if the timer was set prior to it being cleared; otherwise, it is assigned False.

20/3

The function Time_Remaining returns the execution time interval that remains until the timer TM would expire, if that timer is set; otherwise, it returns Time_Span_Zero.

21/2

The constant Min_Handler_Ceiling is the minimum ceiling priority required for a protected object with a handler to ensure that no ceiling violation will occur when that handler is invoked.

22/2

As part of the finalization of an object of type Timer, the timer is cleared.

23/2

For all the subprograms defined in this package, Tasking_Error is raised if the task identified by TM.T.all has terminated, and Program_Error is raised if the value of TM.T.all is Task_Identification.Null_Task_Id.

24/2

An exception propagated from a handler invoked as part of the expiration of a timer has no effect.

Erroneous Execution

25/2

For a call of any of the subprograms defined in this package, if the task identified by TM.T.all no longer exists, the execution of the program is erroneous.

Implementation Requirements

26/2

For a given Timer object, the implementation shall perform the operations declared in this package atomically with respect to any of these operations on the same Timer object. The replacement of a handler by a call of Set_Handler shall be performed atomically with respect to the execution of the handler.

26.a/2
reason

This prevents various race conditions. In particular it ensures that if an event occurs when Set_Handler is changing the handler then either the new or old handler is executed in response to the appropriate event. It is never possible for a new handler to be executed in response to an old event

27/2

When an object of type Timer is finalized, the system resources used by the timer shall be deallocated.

Implementation Permissions

28/3

Implementations may limit the number of timers that can be defined for each task. If this limit is exceeded, then Timer_Resource_Error is raised.

29/2

NOTE A Timer_Handler can be associated with several Timer objects.

Extensions to Ada 95

29.a/2

The package Execution_Time.Timers is new.

D.14.2 Group Execution Time Budgets

1/3

This subclause describes a language-defined package to assign execution time budgets to groups of tasks.

Static Semantics

2/2

The following language-defined library package exists:

3/5

with System; with System.Multiprocessors; package Ada.Execution_Time.Group_Budgets with Nonblocking, Global => in out synchronized is 4/3

type Group_Budget(CPU : System.Multiprocessors.CPU := System.Multiprocessors.CPU'First) is tagged limited private; 5/5

type Group_Budget_Handler is access protected procedure (GB : in out Group_Budget) with Nonblocking => False; 6/2 type Task_Array is array (Positive range <>) of Ada.Task_Identification.Task_Id; 7/2 Min_Handler_Ceiling : constant System.Any_Priority := implementation-defined;

7.a.1/3
implementation defined

The value of Min_Handler_Ceiling in Execution_Time.Group_Budgets.

8/2

procedure Add_Task (GB : in out Group_Budget; T : in Ada.Task_Identification.Task_Id); procedure Remove_Task (GB: in out Group_Budget; T : in Ada.Task_Identification.Task_Id); function Is_Member (GB : Group_Budget; T : Ada.Task_Identification.Task_Id) return Boolean; function Is_A_Group_Member (T : Ada.Task_Identification.Task_Id) return Boolean; function Members (GB : Group_Budget) return Task_Array; 9/2 procedure Replenish (GB : in out Group_Budget; To : in Time_Span); procedure Add (GB : in out Group_Budget; Interval : in Time_Span); function Budget_Has_Expired (GB : Group_Budget) return Boolean; function Budget_Remaining (GB : Group_Budget) return Time_Span; 10/2 procedure Set_Handler (GB : in out Group_Budget; Handler : in Group_Budget_Handler); function Current_Handler (GB : Group_Budget) return Group_Budget_Handler; procedure Cancel_Handler (GB : in out Group_Budget; Cancelled : out Boolean); 11/2 Group_Budget_Error : exception; 12/2 private -- not specified by the language end Ada.Execution_Time.Group_Budgets;

13/2

The type Group_Budget represents an execution time budget to be used by a group of tasks. The type Group_Budget needs finalization (see 7.6). A task can belong to at most one group. Tasks of any priority can be added to a group.

14/2

An object of type Group_Budget has an associated nonnegative value of type Time_Span known as its budget, which is initially Time_Span_Zero. The type Group_Budget_Handler identifies a protected procedure to be executed by the implementation when the budget is exhausted, that is, reaches zero. Such a protected procedure is called a handler.

15/2

An object of type Group_Budget also includes a handler, which is a value of type Group_Budget_Handler. The handler of the object is said to be set if it is not null and cleared otherwise. The handler of all Group_Budget objects is initially cleared.

15.a/2
discussion

Type Group_Budget is tagged. This makes it possible to share a handler between several events. In simple cases, 'Access can be used to compare the parameter with a specific group budget object (this works because a tagged type is a by-reference type). In more complex cases, a type extension of type Group_Budget can be declared; a double type conversion can be used to access the extension data. An example of how this can be done can be found for the similar type Timing_Event, see D.15.

Dynamic Semantics

16/2

The procedure Add_Task adds the task identified by T to the group GB; if that task is already a member of some other group, Group_Budget_Error is raised.

17/2

The procedure Remove_Task removes the task identified by T from the group GB; if that task is not a member of the group GB, Group_Budget_Error is raised. After successful execution of this procedure, the task is no longer a member of any group.

18/3

The function Is_Member returns True if the task identified by T is a member of the group GB; otherwise, it returns False.

19/3

The function Is_A_Group_Member returns True if the task identified by T is a member of some group; otherwise, it returns False.

20/2

The function Members returns an array of values of type Task_Identification.Task_Id identifying the members of the group GB. The order of the components of the array is unspecified.

21/3

The procedure Replenish loads the group budget GB with To as the Time_Span value. The exception Group_Budget_Error is raised if the Time_Span value To is nonpositive. Any execution on CPU of any member of the group of tasks results in the budget counting down, unless exhausted. When the budget becomes exhausted (reaches Time_Span_Zero), the associated handler is executed if the handler of group budget GB is set. Nevertheless, the tasks continue to execute.

22/2

The procedure Add modifies the budget of the group GB. A positive value for Interval increases the budget. A negative value for Interval reduces the budget, but never below Time_Span_Zero. A zero value for Interval has no effect. A call of procedure Add that results in the value of the budget going to Time_Span_Zero causes the associated handler to be executed if the handler of the group budget GB is set.

23/3

The function Budget_Has_Expired returns True if the budget of group GB is exhausted (equal to Time_Span_Zero); otherwise, it returns False.

24/2

The function Budget_Remaining returns the remaining budget for the group GB. If the budget is exhausted it returns Time_Span_Zero. This is the minimum value for a budget.

25/3

The procedure Set_Handler associates the handler Handler with the Group_Budget GB: if Handler is null, the handler of Group_Budget is cleared; otherwise, it is set.

26/2

A call of Set_Handler for a Group_Budget that already has a handler set replaces the handler; if Handler is not null, the handler for Group_Budget remains set.

27/3

The function Current_Handler returns the handler associated with the group budget GB if the handler for that group budget is set; otherwise, it returns null.

28/3

The procedure Cancel_Handler clears the handler for the group budget if it is set. Cancelled is assigned True if the handler for the group budget was set prior to it being cleared; otherwise, it is assigned False.

29/2

The constant Min_Handler_Ceiling is the minimum ceiling priority required for a protected object with a handler to ensure that no ceiling violation will occur when that handler is invoked.

30/2

The precision of the accounting of task execution time to a Group_Budget is the same as that defined for execution-time clocks from the parent package.

31/2

As part of the finalization of an object of type Group_Budget all member tasks are removed from the group identified by that object.

32/3

If a task is a member of a Group_Budget when it terminates, then as part of the finalization of the task it is removed from the group.

33/2

For all the operations defined in this package, Tasking_Error is raised if the task identified by T has terminated, and Program_Error is raised if the value of T is Task_Identification.Null_Task_Id.

34/2

An exception propagated from a handler invoked when the budget of a group of tasks becomes exhausted has no effect.

Erroneous Execution

35/2

For a call of any of the subprograms defined in this package, if the task identified by T no longer exists, the execution of the program is erroneous.

Implementation Requirements

36/2

For a given Group_Budget object, the implementation shall perform the operations declared in this package atomically with respect to any of these operations on the same Group_Budget object. The replacement of a handler, by a call of Set_Handler, shall be performed atomically with respect to the execution of the handler.

36.a/2
reason

This prevents various race conditions. In particular it ensures that if the budget is exhausted when Set_Handler is changing the handler then either the new or old handler is executed and the exhausting event is not lost.

37/2

NOTE 1 Clearing or setting of the handler of a group budget does not change the current value of the budget. Exhaustion or loading of a budget does not change whether the handler of the group budget is set or cleared.

38/2

NOTE 2 A Group_Budget_Handler can be associated with several Group_Budget objects.

Extensions to Ada 95

38.a/2

The package Execution_Time.Group_Budgets is new.

Inconsistencies With Ada 2005

38.b/3

A Group_Budget is now defined to work on a single processor. If an implementation managed to make this package work for programs running on a multiprocessor system, and a program depends on that fact, it could fail when ported to Ada 2012. We believe it is unlikely that such an implementation exists because of the difficulty of signalling other processors when the time reaches zero; in any case, depending on such an implementation is not portable.

D.14.3 Execution Time of Interrupt Handlers

1/3

This subclause describes a language-defined package to measure the execution time of interrupt handlers.

Static Semantics

2/3

The following language-defined library package exists:

3/5

with Ada.Interrupts; package Ada.Execution_Time.Interrupts with Nonblocking, Global => in out synchronized is function Clock (Interrupt : Ada.Interrupts.Interrupt_Id) return CPU_Time; function Supported (Interrupt : Ada.Interrupts.Interrupt_Id) return Boolean; end Ada.Execution_Time.Interrupts;

4/3

The execution time or CPU time of a given interrupt Interrupt is defined as the time spent by the system executing interrupt handlers identified by Interrupt, including the time spent executing run-time or system services on its behalf. The mechanism used to measure execution time is implementation defined. Time spent executing interrupt handlers is distinct from time spent executing any task.

4.a/3
discussion

The implementation-defined mechanism here is the same as that covered by the Documentation Requirements of D.14, so we don't repeat that requirement here.

5/3

For each interrupt, the execution time value is initially set to zero.

Dynamic Semantics

6/3

The function Clock returns the current cumulative execution time of the interrupt identified by Interrupt. If Separate_Interrupt_Clocks_Supported is set to False the function raises Program_Error.

7/3

The function Supported returns True if the implementation is monitoring the execution time of the interrupt identified by Interrupt; otherwise, it returns False. For any Interrupt_Id Interrupt for which Supported(Interrupt) returns False, the function Clock(Interrupt) will return a value equal to Ada.Execution_Time.Time_Of(0).

Extensions to Ada 2005

7.a/3

The package Execution_Time.Interrupts is new.