/* time-schedule.c Programme to test how long a context switch takes. Copyright (C) 1998 Richard Gooch This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. Richard Gooch may be reached by email at rgo...@atnf.csiro.au The postal address is: Richard Gooch, c/o ATNF, P. O. Box 76, Epping, N.S.W., 2121, Australia. */ /* This programme will determine the context switch (scheduling) overhead on a system. It takes into account SMP machines. True context switches are measured. Written by Richard Gooch 15-SEP-1998 Last updated by Richard Gooch 16-SEP-1998 */ #include <unistd.h> #ifndef _REENTRANT # define _REENTRANT #endif #ifndef _POSIX_THREAD_SAFE_FUNCTIONS # define _POSIX_THREAD_SAFE_FUNCTIONS #endif #include <pthread.h> #include <stdlib.h> #include <stdio.h> #include <string.h> #include <signal.h> #include <sched.h> #include <sys/time.h> #include <sys/mman.h> #if 0 /* Set to 1 if you don't have Karma */ # define mt_num_processors() 1 /* Set to the number of processors */ # define ERRSTRING sys_errlist[errno] # define FALSE 0 # define TRUE 1 #else # include <karma.h> # include <karma_mt.h> #endif #define MAX_ITERATIONS 10000 static void hog_other_cpus (); static void run_yielder (int use_threads); static void *yielder_main (void *arg); static void s_term_handler (); static void run_low_priority (unsigned int num); static volatile unsigned int sched_count = 0; int main (int argc, char **argv) { int use_threads = FALSE; unsigned int count; signed long total_diffs; /*signed long diffs[MAX_ITERATIONS];*/ static char *usage = "time-schedule [-h] [-thread] [num_running]"; for (count = 1; count < argc; ++count) { if (strcmp (argv[count], "-h") == 0) { fprintf (stderr, "Usage:\t%s\n", usage); exit (0); } else if (strcmp (argv[count], "-thread") == 0) use_threads = TRUE; else run_low_priority ( atoi (argv[count]) ); } if (geteuid () == 0) { struct sched_param sp; memset (&sp, 0, sizeof sp); sp.sched_priority = 10; if (sched_setscheduler (0, SCHED_FIFO, &sp) != 0) { fprintf (stderr, "Error changing to RT class\t%s\n", ERRSTRING); exit (1); } if (mlockall (MCL_CURRENT | MCL_FUTURE) != 0) { fprintf (stderr, "Error locking pages\t%s\n", ERRSTRING); exit (1); } } else fprintf (stderr, "Not running with RT priority\n"); hog_other_cpus (); run_yielder (use_threads); /*memset (diffs, 0, sizeof diffs);*/ total_diffs = 0; for (count = 0; count < MAX_ITERATIONS; ++count) { int i; signed long diff; struct timeval before, after; gettimeofday (&before, NULL); for (i = 0; i < 10; ++i) sched_yield (); gettimeofday (&after, NULL); diff = 1000000 * (after.tv_sec - before.tv_sec); diff += after.tv_usec - before.tv_usec; diff = diff / 20; /*diffs[count] = diff;*/ total_diffs += diff; } #if 0 for (count = 0; count < MAX_ITERATIONS; count += 500) { printf ("%-8ld us\n", diffs[count]); } #endif printf ("Average scheduling latency: %ld us\n", total_diffs / MAX_ITERATIONS); fflush (stdout); if (use_threads) fprintf (stderr, "Number of yields: %u\n", sched_count); /* Finish up */ kill (0, SIGTERM); return (0); } /* End Function main */ static void hog_other_cpus () /* [SUMMARY] Hog other CPUs with a high-priority job. [RETURNS] Nothing. */ { unsigned int count; for (count = mt_num_processors (); count > 1; --count) { switch ( fork () ) { case 0: /* Child */ while (TRUE); break; case -1: /* Error */ fprintf (stderr, "Error forking\t%s\n", ERRSTRING); kill (0, SIGTERM); break; default: /* Parent */ break; } } fprintf (stderr, "Started %u hog processes\n", mt_num_processors () - 1); } /* End Function hog_other_cpus */ static void run_yielder (int use_threads) /* [SUMMARY] Run other process which will continuously yield. <use_threads> If TRUE, the yielding process is just a thread. [RETURNS] Nothing. */ { struct sigaction new_action; pthread_t thread; if (use_threads) { if (pthread_create (&thread, NULL, yielder_main, NULL) != 0) { fprintf (stderr, "Error creating thread\t%s\n", ERRSTRING); kill (0, SIGTERM); } fprintf (stderr, "Started yielder thread\n"); return; } switch ( fork () ) { case 0: /* Child */ break; case -1: /* Error */ fprintf (stderr, "Error forking\t%s\n", ERRSTRING); kill (0, SIGTERM); break; default: /* Parent */ fprintf (stderr, "Started yielder process\n"); return; /*break;*/ } memset (&new_action, 0, sizeof new_action); sigemptyset (&new_action.sa_mask); new_action.sa_handler = s_term_handler; if (sigaction (SIGTERM, &new_action, NULL) != 0) { fprintf (stderr, "Error setting SIGTERM handler\t%s\n", ERRSTRING); exit (1); } yielder_main (NULL); } /* End Function run_yielder */ static void *yielder_main (void *arg) /* [SUMMARY] Yielder function. <arg> An arbitrary argument. Ignored. [RETURNS] NULL. */ { while (TRUE) { sched_yield (); ++sched_count; } } /* End Function yielder_main */ static void s_term_handler () { fprintf (stderr, "Number of yields: %u\n", sched_count); exit (0); } /* End Function s_term_handler */ static void run_low_priority (unsigned int num) /* [SUMMARY] Run low priority processes. <num> Number of processes. [RETURNS] Nothing. */ { fprintf (stderr, "Starting %u low priority processes\n", num); for (; num > 0; --num) { switch ( fork () ) { case 0: /* Child */ if (nice (10) != 0) { fprintf (stderr, "Error nicing\t%s\n", ERRSTRING); kill (0, SIGTERM); } while (TRUE) sched_yield (); break; case -1: /* Error */ fprintf (stderr, "Error forking\t%s\n", ERRSTRING); kill (0, SIGTERM); break; default: /* Parent */ break; } } } /* End Function run_low_priority */