3.1 General-Purpose Libraries
3.1.1 Global Utilities
3.1.2 Numbers
3.1.3 Strings
3.1.4 Booleans
3.1.5 option
3.1.6 either
3.1.7 pick
3.1.8 lists
3.1.9 sets
3.1.10 arrays
3.1.11 string-dict
3.1.12 Tables
3.1.13 gdrive-sheets
3.1.14 csv
3.1.15 color
3.1.16 The image libraries
3.1.17 reactors
3.1.18 chart
3.1.19 plot
3.1.20 statistics
3.1.21 math
3.1.22 matrices
3.1.23 Timing
3.1.24 fetch
On this page:
3.1.23.1 Background and Caveat
3.1.23.2 Design and Rationale
3.1.23.3 Functions
time-only
time-value
time-now
3.1.23 Timing🔗

Usage:
include timing
import timing as ...

3.1.23.1 Background and Caveat🔗

This library gives you the ability to determine how long a computation takes.

Please note that measuring the time of a computation can be very complicated. Naturally, the same computation may take very different amounts of time on different machines. However, it may also take very different amounts of time on the same machine, depending on what else is running. It may take very different amounts of time even if nothing else is running (are you sure?), because of cache effects, JIT effects, and so on.

Therefore, you should either:
  • only use this library in an advisory way, not treat its answers as being too precise; and/or

  • perform timing many times and use appropriate statistical methods to get a better sense of the true running time

Nevertheless, this library can be very useful for rough approximation and for telling apart coarse differences in running time.

3.1.23.2 Design and Rationale🔗

The timing functions all take thunks, i.e., procedures of no argument, to represent the computation that needs to be timed.

It is important to understand why this is. Suppose we want to know how long it takes to run some computation, f(x). Pretend we had a function called time-of and could just call time-of(f(x)). We might expect this to determine how long it takes for f(x) to run.

Unfortunately, that is not how functions work in Pyret (or indeed in most programming languages: see the Standard Model of Languages). Instead, Pyret first turns f(x) into a value (if it has one), and it is this value that is supplied to time-of(…). Therefore, time-of already receives a value, which takes effectively no time to compute; so no matter how long f(x) took, time-of(f(x)) will return essentially the same answer, which will effectively be 0.

In contrast, a thunk reflects the computation rather than its answer. When this thunk is passed to a timing function, the computation has not yet occurred. Instead, the timing function can call it in an appropriate way, and can thus measure how long it ran.

All the functions rely on the underlying system timers, which use Unix epoch time. There are many online converters that will translate the resulting value into something human-readable.

3.1.23.3 Functions🔗

This library provides three functions. time-now is a low-level primitive useful for building the other two. Most of the time you would not use it directly, but we provide it in case you need fine-grained control that doesn’t easily fit the other two.

The other two functions determine how long a given thunk takes to run by running it. They differ in what they produce as a result. One only produces the time that it takes (not to be confused with the value produced by the thunk, which may also be a number, and even a positive integer that is similar to the returned time); the other produces both the duration and the value produced by the computation.

time-only :: (f :: ( -> T)) -> Number

Consumes a thunk, runs it, and produces how long it takes to run (in milliseconds).

Examples:

include timing check: produce-0-to-9 = {(): range(0, 10)} fun takes-less-than-1-second(t): t < 1000 end # NOTE: may fail on a sufficiently slooooow machine! time-only(produce-0-to-9) satisfies takes-less-than-1-second end

time-value :: (f :: ( -> T)) -> {Number; T}

Consumes a thunk, runs it, and produces both how long it takes to run (in milliseconds) and the value that it produces.

Examples:

include timing check: produce-0-to-9 = {(): range(0, 10)} fun takes-less-than-1-second(t): t < 1000 end {t; v} = time-value(produce-0-to-9) # NOTE: may fail on a sufficiently slooooow machine! t satisfies takes-less-than-1-second v is [list: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9] end

time-now :: () -> Number

Returns the current time, in milliseconds since the start of the Unix epoch. Since this value changes every millisecond, it is very difficult to write a test where it succeeds; it’s much easier to write a test that it fails.

Examples:

include timing check: time-now() is-not 1740426758012 end

We can therefore reconstruct, for instance, time-only as follows:

Examples:

fun my-time-only(thunk): start-time = time-now() _ = thunk() end-time = time-now() end-time - start-time end