Timing utilities are very dependent on the system api provided for their use. This package provides a means for handling different timing models. Current support - posix rt : complete. - macosx : posix timers only, missing absolute timers. - win : none.
Timing utilities are very dependent on the system api provided for their use.
This package provides a means for handling different timing models.
Posix timers record times as a pair : (seconds,nanoseconds), so they have a
very fine resolution, but it needs to remembered that the system latency
will still affect their results. For example, sleeping for 1nanosecond
wont work as you expect, since the system latency will usually mean the
command has to wait for the scheduler to grant it a slice of the action
(usually a wait of around 1ms on a linux desktop). This latency is usually
configured by the scheduler being used by the kernel.
Returning to the pair, the first unit, seconds is usually of a system defined
type. This allows it to be extendable to a larger type in the future if
the system time requires a larger value. The latter is measured with the
long variable, which is more than sufficient to cater for 1x10^9 units.
MacOSX timers are mostly posix, however they are missing the extensions provided by the librt library, e.g. clock_gettime and clock_nanosleep. The real downside of this is that it is not possible to use absolute time with the macosx posix timers (i.e. periodic timers will drift). There are another set of mac specific timers that will let you do this, but it has not yet been implemented.
I haven't yet implemented these.
Include the following at the top of any translation unit that uses
time functions or classes.
@code
#include <ecl/time.hpp>
// The time classes
using ecl::CpuWatch;
using ecl::Duration;
using ecl::MicroSleep;
using ecl::MilliSleep;
using ecl::NanoSleep;
using ecl::Sleep;
using ecl::Snooze;
using ecl::StopWatch;
using ecl::TimeData;
using ecl::TimeStamp;
@endcode
You will also need to link to <i>-lecl_time</i>.
@subsection Timestamps
Timestamps provide a means of doing one of two things:
- Recording the system time since some reference point.
- Manually recording a timestamp, or time duration (e.g. result of a timestamp difference operation).
There are several ways of initialising the timestamp:
@code
TimeStamp time_system; // Automatically captures system time since Epoch.
TimeStamp time_double(3.21) // Initialise with a decimalised time (slow).
TimeStamp time_pair(3,210000000) // Initialise with a (s,ns) pair (fast for posix rt systems).
@endcode
These operations can also be performed to set the timestamp after construction:
@code
time_system.stamp(); // Automatically captures system time since Epoch.
time_double.stamp(3.21) // Initialise with a decimalised time (slow).
time_pair.stamp(3,210000000) // Initialise with a (s,ns) pair (fast for posix rt systems).
@endcode
All the usual comparison (==,!=,<=,>=,<,>) and mathematical (+,-,+=,-=) operations can also be used.
@code
if ( time_system > time_double ) {
time_system -= time_double;
}
@endcode
Streaming:
@code
TimeStamp timestamp; // captures current time
std::cout << timestamp << std::endl; // 1682346.235653090
@endcode
<b>Caution</b>: The only thing to be wary of with timestamps is to remember that they
must always be positive. This was a design decision that keeps the timestamp class as light as
possible. If negativity was introduced, an extra sign bit would be required, and in almost all
timestamp operations, this is not necessary.
<b>Exceptions</b>: The timestamp class will throw exceptions (in debug mode only) whenever a
timestamp method is used that would create a negative timestamp.
@subsection Duration
Durations are intended to intuitively represent the passage of time. Although is is conceptually
different from a timestamp, the functionality under the hood is currently identical. Subsequently,
the @ref ecl::TimeStamp "Timestamp" class is currently typedef'd to the @ref ecl::Duration
"Duration" class.
This might change at some point in the future if we require negativity (as mentioned above).
@subsection StopWatch
This is a fairly intuitive class and uses the TimeStamp class under the hood for recording
times.
Note that the stopwatch starts automatically, just use restart() if you
wish to reset and start again.
@code
StopWatch stopwatch;
TimeStamp time;
time = stopwatch.split()
cout << time << endl;
time = stopwatch.elapsed()
cout << time << endl;
stopwatch.restart();
@endcode
@subsection CpuWatch
This is a variation of the stopwatch for systems with librt. The time measured by the
cpu watch is not system time, rather the time spent by the process actually exeucting
on the cpu. This is very useful for benchmarking programs. Usage is exactly the same as for
the stopwatch.
@subsection Sleep
Some simple sleep classes. They can be used directly or preconfigured.
@code
Duration duration(1,300000000);
Sleep sleep;
MilliSleep sleep_ms;
MicroSleep sleep_us;
NanoSleep sleep_ns;
// Direct sleep commands
sleep(duration);
sleep(3);
sleep_ms(3500);
sleep_us(500000);
sleep_ns(500000000);
// Preconfigured (i.e. use last configured command)
MilliSleep pc_sleep_ms(duration);
sleep_ms(); // uses the last configuration
pc_sleep_ms(); // uses the constructor configuration
@endcode
@subsection Snooze
Snooze is a periodic sleeper, useful in control loops where you want to exactly control the
time taken by each loop. Using a regular sleep function to do this can cause some time
drift problems (refer to the snooze class documentation for more detail). The snooze
class gets around this by setting its periodic timestamps off the absolute clock time
rather than calculating relative time splits.
@code
Snooze snooze(Duration(0,20000000); // 20ms snooze
// Some preliminaries
snooze.initialise(); // make sure the snoozer is sync'ed with the current time.
while (1) {
// do some work
snooze();
}
@endcode
Note, this can be problematic if the periodic timestamps get behind the current time. To
remedy this, by default the class validates the timestamp at each check and syncs it with
the current time if it starts to lag behind (can easily be caused by the system scheduler
knocking it out of whack in favour of another process). This can be manually turned
off in the constructor if you're confident of the timing processes and this will marginally
reduce the snoozer's latency.
@subsection TimeData
This is a benchmarking utility for storing timings and then analysing them with some statistical
methods.
@code
CpuWatch cpuwatch;
TimeData timings;
ecl::Duration duration;
for ( unsigned int i = 0; i < 100; ++i ) {
cpuwatch.restart();
// do some work here
duration = cpuwatch.split();
timings.push_back(duration);
}
std::cout << "Average : " << timings.average() << std::endl;
std::cout << "Variance: " << timings.variance() << std::endl;
@endcode
- src/test/cpuwatch_rt.cpp
- src/test/sleep.cpp
- src/test/snooze.cpp
- src/test/stopwatch.cpp
- src/test/timestamp.cpp
- src/test/time_data.cpp
- src/examples/sleep.cpp
- /ecl_core_apps/src/benchmarks/snooze.cpp : benchmarks the latencies for the periodic timer.
- @ref changelog "ChangeLog"
The cross-platform support, particularly the macosx absolute timers really needs to be done.