Last active
November 14, 2018 07:25
-
-
Save bschlinker/7428815 to your computer and use it in GitHub Desktop.
System Timer Granularity Analyzer for C
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// System Timer Granularity Analysis | |
// Developed by: Brandon C. Schlinker (Inbound5 / Develop5) | |
#include <stdio.h> | |
#include <stdlib.h> | |
#include <string.h> | |
#include <inttypes.h> | |
#include <signal.h> | |
#include <sched.h> | |
#include <unistd.h> | |
#include <sys/timerfd.h> | |
#include <sys/prctl.h> | |
#define __STDC_FORMAT_MACROS | |
// generate statistics on Ctrl+C / SIG_INT | |
int terminate; | |
void handle_sigint (int signum) | |
{ | |
terminate = 1; | |
} | |
// perform timer accuracy analysis | |
int main(int argc, char *argv[]) | |
{ | |
// make sure we have a sufficient # of arguments | |
if(argc < 3) { | |
printf("%s: [INTERVAL_IN_NANOSECONDS] [MAX_NUMBER_OF_QUEUED_EVENTS] \n", argv[0]); | |
return(-1); | |
} | |
// grab user parameters | |
int nanosecondInterval = atoi(argv[1]); | |
int maximumNumOfQueuedEventsTolerated = atoi(argv[2]); | |
// set real-time priority | |
struct sched_param schedparm; | |
memset(&schedparm, 0, sizeof(schedparm)); | |
schedparm.sched_priority = 99; // highest rt priority | |
sched_setscheduler(0, SCHED_FIFO, &schedparm); | |
// set timer slack equal to 1 nanosecond | |
prctl(PR_SET_TIMERSLACK, 1); | |
// timer setup | |
int timerfd = timerfd_create(CLOCK_MONOTONIC,0); | |
struct itimerspec timspec; | |
bzero(&timspec, sizeof(timspec)); | |
timspec.it_interval.tv_sec = 0; | |
timspec.it_interval.tv_nsec = nanosecondInterval; | |
timspec.it_value.tv_sec = 0; | |
timspec.it_value.tv_nsec = 1; | |
timerfd_settime(timerfd, 0, &timspec, 0); | |
// statistics tracking | |
int * eventQueueLength = malloc(sizeof(uint64_t) * maximumNumOfQueuedEventsTolerated); | |
uint64_t totalInstancesOfMissedEvents = 0; | |
uint64_t totalNumOfMissedEvents = 0; | |
uint64_t eventsQueued = 0; | |
uint64_t eventsProceessed = 0; | |
// termination tracking | |
terminate = 0; | |
signal (SIGINT, handle_sigint); | |
// poll the timerfd, if we missed an expiration, increment | |
while( read (timerfd, &eventsQueued, sizeof(eventsQueued) )){ | |
if(terminate == 1) { break; } | |
else if(eventsQueued > maximumNumOfQueuedEventsTolerated) { break; } | |
else if(eventsQueued > 1) | |
{ | |
totalInstancesOfMissedEvents++; | |
totalNumOfMissedEvents+=(eventsQueued - 1); // -1 as the current read isn't an expiration tolerated | |
eventQueueLength[eventsQueued - 1]++; | |
} | |
else { eventsProceessed++; } | |
} | |
// calculate baseline statistics | |
uint64_t totalNumOfEvents = eventsProceessed + totalNumOfMissedEvents; | |
float percentMissingEvents = ((float)totalNumOfMissedEvents / (float)totalNumOfEvents) * 100; | |
// calculate mean and median queue length when event(s) missed | |
float averageNumOfMissedEventsPerFailureInstance = 0; | |
uint64_t medianNumOfMissedEventsPerFailureInstance = 0; | |
{ | |
int i = 1; | |
for(i = 1; i < maximumNumOfQueuedEventsTolerated; i++) | |
{ | |
medianNumOfMissedEventsPerFailureInstance+= (eventQueueLength[i]); | |
if(medianNumOfMissedEventsPerFailureInstance >= (totalInstancesOfMissedEvents / 2)) // rounds median up | |
{ | |
medianNumOfMissedEventsPerFailureInstance = i; | |
break; | |
} | |
} | |
} | |
averageNumOfMissedEventsPerFailureInstance = ((float)totalNumOfMissedEvents / (float)totalInstancesOfMissedEvents); | |
// print statistics | |
printf("\n\nStatistics:\n"); | |
printf("latest number of missed events = %" PRIu64 " | (limit = %d per instance or program quits)\n", (eventsQueued - 1), maximumNumOfQueuedEventsTolerated); | |
printf("total number of missed events = %" PRIu64 "\n", totalNumOfMissedEvents); | |
printf("total instances of missed events = %" PRIu64 "\n", totalInstancesOfMissedEvents); | |
printf("(one instance can be associated with multiple missed events)\n"); | |
printf("total number of on-time events = %" PRIu64 "\n", eventsProceessed); | |
printf("total number of events = %" PRIu64 "\n", totalNumOfEvents); | |
printf("%f %% of readings were not processed (late)\n", percentMissingEvents); | |
printf("average number of missed events per instance of missed events = %f\n", averageNumOfMissedEventsPerFailureInstance); | |
printf("median number of missed events per instance of missed events = %" PRIu64 "\n", medianNumOfMissedEventsPerFailureInstance); | |
// dump the array | |
free(eventQueueLength); | |
// return success | |
return(0); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Hi
Can you briefly explain the working of this program? I am looking for a scheduler that will help to schedule some jobs every 1 ms with utmost accuracy. Do you have any idea how to approach that problem?