Video games are written as a main loop: process player input, update the state of the game, render a new frame to the screen, repeat. They do this 60 times a second, with millisecond timing. Most monitoring tools are also written as loops: send a probe, wait for the response, update a data store, sleep. Often this is done pretty slowly, maybe once a second! In video games if you can’t update fast enough, you skip the rendering step and the frame rate drops. With monitoring tools if your loop takes to long you also stop logging data as often, and instead of choppy gameplay you get gaps in your graphs, often when you need that data the most!
Let’s use ping as an example and see how we can rewrite its main loop to function more like a video game, keeping a high frame rate.
3. /*
* P I N G . C
*
* Using the InterNet Control Message Protocol (ICMP) "ECHO" facility,
* measure round-trip-delays and packet loss across network paths.
*
* Author -
* Mike Muuss
* U. S. Army Ballistic Research Laboratory
* December, 1983
* Modified at Uc Berkeley
* Record Route and verbose headers - Phil Dykstra, BRL, March 1988.
*
* Status -
* Public Domain. Distribution Unlimited.
*
* Bugs -
* More statistics could always be gathered.
* This program has to run SUID to ROOT to access the ICMP socket.
*/
4.
5.
6. Raspberry Pi Model B+ (700 MHz): Dhrystones per Second: 1481481
VAX MIPS rating = 843.19
Raspberry Pi 2 Model B (1000 MHz)*: Dhrystones per Second: 2085024
VAX MIPS rating = 1186.70
*using one core
9. NAME
ping - send ICMP ECHO_REQUEST packets to network hosts
SYNOPSIS
ping [-dfnqrvR] [-c count] [-i wait] [-l preload] [-p pattern] [-s packetsize]
OPTIONS
-f
Flood ping. Outputs packets as fast as they come back or one hundred times per
second, whichever is more. For every ECHO_REQUEST sent a period ``.'' is printed,
while for every ECHO_REPLY received a backspace is printed. This provides a rapid
display of how many packets are being dropped. Only the super-user may use this
option. This can be very hard on a network and should be used with caution.
-i wait
Wait wait seconds between sending each packet. The default is to wait for one
second between each packet. This option is incompatible with the -f option.
11. /*
* C A T C H E R
*
* This routine causes another PING to be transmitted, and then
* schedules another SIGALRM for 1 second from now.
*
* Bug -
* Our sense of time will slowly skew (ie, packets will not be launched
* exactly at 1-second intervals). This does not affect the quality
* of the delay and loss statistics.
*/
catcher()
{
if (nreceived) {
waittime = 2 * tmax / 1000;
if (waittime == 0)
waittime = 1;
} else
waittime = PING_MAXWAIT; // 10
signal(SIGALRM, finish);
alarm(waittime);
}
}
12. NAME
alarm - schedule an alarm signal
SYNOPSIS
#include <unistd.h>
unsigned alarm(unsigned seconds);
DESCRIPTION
The alarm() function shall cause the system to generate a SIGALRM signal for the process after the number of realtime seconds
specified by seconds have elapsed. Processor scheduling delays may prevent the process from handling the signal as soon as it is
generated.
14. NAME
getitimer, setitimer - get and set value of interval timer
SYNOPSIS
#include <sys/time.h>
int setitimer(int which, const struct itimerval *restrict value, struct itimerval *restrict ovalue);
DESCRIPTION
The setitimer() function shall set the timer specified by which to the value specified in the structure pointed to by value, and if ovalue
is not a null pointer, store the previous value of the timer in the structure pointed to by ovalue.
A timer value is defined by the itimerval structure, specified in <sys/time.h>. If it_value is non-zero, it shall indicate the time to the
next timer expiration. If it_interval is non-zero, it shall specify a value to be used in reloading it_value when the timer expires.
An XSI-conforming implementation provides each process with at least three interval timers, which are indicated by the which
argument:
ITIMER_PROF
Decrements both in process virtual time and when the system is running on behalf of the process. It is designed to be
used by interpreters in statistically profiling the execution of interpreted programs. Each time the ITIMER_PROF timer expires,
the SIGPROF signal is delivered.
ITIMER_REAL
Decrements in real time. A SIGALRM signal is delivered when this timer expires.
ITIMER_VIRTUAL
Decrements in process virtual time. It runs only when the process is executing. A SIGVTALRM signal is delivered when
it expires.
25. NAME
fping - send ICMP ECHO_REQUEST packets to network hosts
SYNOPSIS
fping [ options ] [ systems... ]
OPTIONS
-i n The minimum amount of time (in milliseconds) between sending a ping packet to any
target (default is 25).
-p <n> In looping or counting modes (-l, -c, or -C), this parameter sets the time in
milliseconds that fping waits between successive packets to an individual target.
Default is 1000.
EXAMPLES
Generate 20 pings to two hosts in ca. 1 second (i.e. one ping every 50 ms to each host),
and report every ping RTT at the end:
$ fping −q −i=1 −C=20 −p=50 127.0.0.1 127.0.0.2
26. NAME
nfsping - send RPC NULL requests to NFS servers
SYNOPSIS
nfsping [-aAdDEGhKlLmMnNqRsTuv] [-c count] [-C count] [-g prefix] [-H hertz] [-i
interval] [-P port] [-Q interval ] [-S source] [-ttimeout] [-V version] <servers...>
OPTIONS
-H
The polling frequency in Hertz. This is the number of pings sent to each target per
second. Default = 10.
27. NFStash/src/nfsping.c:
/* calculate the sleep_time based on the frequency */
/* check for a frequency of 1, that's a simple case */
/* this doesn't support frequencies lower than 1Hz */
if (hertz == 1) {
sleep_time.tv_sec = 1;
sleep_time.tv_nsec = 0;
} else {
sleep_time.tv_sec = 0;
/* nanoseconds */
sleep_time.tv_nsec = 1000000000 / hertz;
}
28. NFStash/src/nfsping.c:
/* sleep between rounds */
/* measure how long the current round took, and subtract that from the sleep time */
/* this tries to ensure that each polling round takes the same time */
#ifdef CLOCK_MONOTONIC_RAW
clock_gettime(CLOCK_MONOTONIC_RAW, &loop_end);
#else
clock_gettime(CLOCK_MONOTONIC, &loop_end);
#endif
timespecsub(&loop_end, &loop_start, &loop_elapsed);
debug("Polling took %lld.%.9ldsn", (long long)loop_elapsed.tv_sec,loop_elapsed.tv_nsec);
/* don't sleep if we went over the sleep_time */
if (timespeccmp(&loop_elapsed, &sleep_time, >)) {
debug("Slow poll, not sleepingn");
} else {
timespecsub(&sleep_time, &loop_elapsed, &sleepy);
debug("Sleeping for %lld.%.9ldsn", (long long)sleepy.tv_sec, sleepy.tv_nsec);
nanosleep(&sleepy, NULL);
}
29. NAME
nanosleep - high resolution sleep (REALTIME)
SYNOPSIS
#include <time.h>
int nanosleep(const struct timespec *rqtp, struct timespec *rmtp);
DESCRIPTION
The nanosleep() function shall cause the current thread to be suspended from execution until either the time interval specified by the
rqtp argument has elapsed or a signal is delivered to the calling thread, and its action is to invoke a signal-catching function or to
terminate the process. The suspension time may be longer than requested because the argument value is rounded up to an integer
multiple of the sleep resolution or because of the scheduling of other activity by the system. But, except for the case of being
interrupted by a signal, the suspension time shall not be less than the time specified by rqtp, as measured by the system clock
CLOCK_REALTIME.
The use of the nanosleep() function has no effect on the action or blockage of any signal.
30. NFStash/src/nfsping.c:
/* grab the wall clock time for output */
/* use the start time of the request */
/* the call_start timer is more important so do this first so we're not measuring the
time this call takes */
clock_gettime(CLOCK_REALTIME, &wall_clock);
/* first time marker */
/* the MONOTONIC clocks don't record the actual time but are good for measuring elapsed
time accurately */
#ifdef CLOCK_MONOTONIC_RAW
clock_gettime(CLOCK_MONOTONIC_RAW, &call_start);
#else
clock_gettime(CLOCK_MONOTONIC, &call_start);
#endif
/* the actual ping */
status = nfsproc3_null_3(NULL, target->client);
/* second time marker */
#ifdef CLOCK_MONOTONIC_RAW
clock_gettime(CLOCK_MONOTONIC_RAW, &call_end);
#else
clock_gettime(CLOCK_MONOTONIC, &call_end);
#endif
31. NAME
clock_getres, clock_gettime, clock_settime - clock and timer functions (REALTIME)
SYNOPSIS
#include <time.h>
int clock_getres(clockid_t clock_id, struct timespec *res);
int clock_gettime(clockid_t clock_id, struct timespec *tp);
int clock_settime(clockid_t clock_id, const struct timespec *tp);
DESCRIPTION
The clock_gettime() function shall return the current value tp for the specified clock, clock_id.
A clock may be system-wide (that is, visible to all processes) or per-process (measuring time that is meaningful only within a
process). All implementations shall support a clock_id of CLOCK_REALTIME as defined in <time.h>. This clock represents the realtime
clock for the system. For this clock, the values returned by clock_gettime() and specified by clock_settime() represent the amount of
time (in seconds and nanoseconds) since the Epoch. An implementation may also support additional clocks.
Setting the value of the CLOCK_REALTIME clock via clock_settime() shall have no effect on threads that are blocked waiting for a
relative time service based upon this clock, including the nanosleep() function; nor on the expiration of relative timers based upon
this clock. Consequently, these time services shall expire when the requested relative interval elapses, independently of the new or
old value of the clock.
If the Monotonic Clock option is supported, all implementations shall support a clock_id of CLOCK_MONOTONIC defined in <time.h>.
This clock represents the monotonic clock for the system. For this clock, the value returned by clock_gettime() represents the
amount of time (in seconds and nanoseconds) since an unspecified point in the past (for example, system start-up time, or the
Epoch). This point does not change after system start-up time. The value of the CLOCK_MONOTONIC clock cannot be set via
clock_settime().
32. CLOCK_MONOTONIC
Clock that cannot be set and represents monotonic time since
some unspecified starting point. This clock is not affected
by discontinuous jumps in the system time (e.g., if the system
administrator manually changes the clock), but is affected by
the incremental adjustments performed by adjtime(3) and NTP.
CLOCK_MONOTONIC_RAW (since Linux 2.6.28; Linux-specific)
Similar to CLOCK_MONOTONIC, but provides access to a raw hard ‐
ware-based time that is not subject to NTP adjustments or the
incremental adjustments performed by adjtime(3).