Timing

When running statements within Magma in serial mode only, using the CPU time to measure the time taken between one or more statements is reasonable and accurate. In particular, one can: (1) use the time statement; (2) call the function Cputime() which returns the total CPU time (in seconds) taken by Magma since startup; (3) call the function Cputime(T) to give the CPU time (in seconds) taken since the variable T was last set to Cputime().

However, when parallelism is used with multiple threads and/or distributed computation, the CPU time is not useful as a measure of the time taken, since this the value which the kernel yields for this typically includes the sum of the CPU time taken by all threads together with extra overhead in the kernel handling, and this number can be skewed even more if hyperthreading is present. So the real (wall-clock) time taken is usually not related to the CPU time divided by the total number of threads in any useful or accurate way. Also, in the distributed case, the CPU time can only refer to threads running on the master node, and not on worker nodes.

Consequently, the real (wall-clock) time taken by one or more statements in Magma is the best way to measure the time taken when parallelism is present. One way of obtaining this information is to call the function Realtime(T) to give the real time taken (in seconds) since T was last set to Realtime(). But it is easier just to use the time statement or the Time function, since these give useful timing information no matter whether serial or parallelism is present or not in any computation. These can be used as follows:

(i)
If time is placed before a statement and a multi-threaded/distributed algorithm is not used within the executed statement, then the CPU time alone is printed.

(ii)
If time is placed before a statement and a multi-threaded/distributed algorithm is used within the executed statement while the real time r taken is less than the CPU time c taken, then the time printed is r, and the tag [r] is appended (to indicate that the time printed is a real time); otherwise the CPU time c is printed. So one can know whether a multi-threaded/distributed algorithm has been used within the executed statement: that is the case if and only if the tag [r] is printed.

(iii)
The verbose time statement vtime has the same behaviour as time in the above two cases.

(iiv)
The function Time is used in a similar way to Cputime and Realtime, while selecting their behaviour depending on whether parallelism is used: after setting a variable such as T to Time(), then after a series of statements, one can print the value Time(T) (which is always a string) to give the lapsed time since T was assigned. Just as for the time statement above, the tag [r] is added if and only if the real time is used in the second call to Time. So the presence of that tag indicates that at least one multi-threaded/distributed algorithm has been used between the calls to Time, while the absence of the tag indicates that no parallelism was used and the CPU time is returned by the Time function.

Example Par_Time1 (H5E35)

A simple example of the use of time and Time is the following.
> SetNthreads(1); // SERIAL (NO PARALLELISM)
> X := Random(MatrixRing(GF(5), 10000));
> time P := X*X; // serial only, so prints CPU time (seconds)
Time: 4.060
> T := Time(); P := X*X; Time(T); // same as what time statement prints
4.060
> SetNthreads(8); // PARALLEL (8 POSIX THREADS)
> time P := X*X;  // parallel, so real time shown by [r] (seconds)
Time: 1.050[r]
> T := Time(); P := X*X; Time(T); // same as what time statement prints
1.050[r]
V2.28, 13 July 2023