Interesting scheduling times

Richard Gooch (rgooch@atnf.csiro.au)
Thu, 17 Sep 1998 00:43:14 +1000


Hi, all. I've been playing around with measuring Linux context
switch times, and I noticed something curious: a Pentium/MMX 200 is
doing much better than a PPro 180. Furthermore, a PPro 180 isn't doing
heaps better than a Pentium 100.

CPU process switch thread switch Kernel version
Pentium 100 12 12 2.1.109
PPro 180 8 4 2.1.122-pre2
Pentium/MMX 200 4 2 2.1.104

all times in microseconds for UP machines.

Do these times seem a little odd to people?
FYI: I've appended my testcode.

Regards,

Richard....
===============================================================================
/* 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 rgooch@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 */

-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.rutgers.edu
Please read the FAQ at http://www.tux.org/lkml/