MCS-378 Lab 1: Scheduling Experiments, Fall 2009

Due: October 14, 2009

Introduction

In this lab, you will experiment with programs that load a Linux system up with processor and/or filesystem activity. My instructions assume you will be doing this on the netbook you have been issued, running the Ubuntu Netbook Remix distribution of Linux.

The two programs you will use are runner.cpp and writer.cpp; each is linked into the web version of this assignment. One does lots of running and the other does lots of writing to a file. To compile them you would use the commands

g++ -o runner runner.cpp
g++ -o writer writer.cpp

To run the runner (for example) you would use

./runner

More typically, you will want to run the program with specific scheduler parameters; you can do this using the schedtool program; specific examples are mentioned below. (Whenever I mention a program such as schedtool that you are unfamiliar with, it might make sense to look at the manpage documenting it.)

There is almost zero programming to do in this lab and the experimental procedure is relatively simple, so the key is going to be to write a good lab report that reports what you observed and provides some interpretation of those observations.

Characterizing Your Experimental Environment

You are conducting a scientific experiment and writing a scientific report. As such, it is important that you report not only the data you obtain (and your interpretation of it), but also the conditions under which that data was obtained. This allows readers to more fully understand your results, and it also provides the necessary information for anyone who wants to replicate or extend your experiment.

One aspect of your experimental environment is how much else your computer is running that might be perturbing the results of your experiment. You can decide whether you want to conduct the experiments with your system booted up normally, with the graphical user interface running, or in the "recovery mode" root shell, which is a less convenient working environment but has many fewer background threads running, and so will produce more tightly controlled results. My instructions assume you can have multiple shell windows open. If you use the recovery mode root shell, the easiest way to achieve that would be using the screen program. Or we can talk about alternatives that don't require multiple shell windows. Even if you run the experiments in a more normal environment, you might want to take some steps to minimize unpredictable background activity; for example, you could turn networking off.

Your report should specify the exact version of Linux you used, and a reasonable level of detail about the hardware: processor model name and clock speed, memory size, and disk drive model. You can find these pieces of information in /proc/version, /proc/cpuinfo, /proc/meminfo, and /proc/scsi/scsi. Ask if you need help locating the appropriate information.

One complication involves the clock speed; this can dynamically change depending on the level of system loading. One option would be to look at it while your system is running an experimental workload, rather than only when idle. Another option would be to set your system to always stay at its maximum clock speed, using the following commands:

echo performance >/sys/devices/system/cpu/cpu0/cpufreq/scaling_governor
echo performance >/sys/devices/system/cpu/cpu1/cpufreq/scaling_governor

If you are not running in a root shell, you will need to use sudo to run the commands as root (i.e., with "super user" system administrator privileges):

sudo bash -c 'echo performance >/sys/devices/system/cpu/cpu0/cpufreq/scaling_governor'
sudo bash -c 'echo performance >/sys/devices/system/cpu/cpu1/cpufreq/scaling_governor'

Another complication is that by default, the Linux kernel runs in "nohz" mode, which seems to introduce some extra latencies that can complicate your performance measurements. To get data that doesn't include this effect, you can edit the kernel boot command line (in grub) to include the option nohz=off.

Experimental Procedure

  1. Run one, two, three, and four copies of runner and observe how long they report they are taking (as a function of how many there are). Also, in yet another shell window, run the top program and see what percentage of the CPU each runner thread is getting (again, as a function of how many there are).

  2. Repeat the prior experiment, but this time use schedtool when you start each runner to specify that it should run on processor 0:

    schedtool -a 0 -e ./runner
    

    To minimize interference, you might want to use schedtool in an analogous way to keep top on processor 1.

  3. The update to chapter 3 explains that as of kernel version 2.6.23, CPU-bound threads share a processor in a ratio that depends only on the difference in their niceness levels. For example, if the threads differ by 5 niceness points, then they run in approximately a 3-to-1 ratio. Test whether this is still true in the kernel version you are using. You can use schedtool to specify the niceness level for a runner, for example

    schedtool -n 5 -e ./runner
    

    to run a runner at niceness 5. As always, if you are having trouble understanding your results, talk with me. If your initial results are peculiar, that may also suggest you conduct some follow-up experiments. If you want to use a negative niceness level and are not running in a root shell, you will have to preface the command with sudo.

  4. Run one copy of writer and observe its reported times, top's report of its CPU percentage, and vmstat's report of the blocks written out per second. To run vmstat, in another shell window give the command

    vmstat 5
    

    The first line of statistics is since the machine was booted, and isn't useful. However, thereafter a new line will be output every 5 seconds (since you specified 5) reporting on activity in the preceding 5 seconds. The column headed "bo" is the one showing blocks written out per second. Is the writer doing lots of actual writing to disk? (You can also look at the machine's disk light.)

  5. Follow up on the preceding question by reading the man page for the fdatasync system call, which ensures data written to a file descriptor is actually written to the disk drive. Insert an appropriate call to fdatasync in the inner loop body of writer.cpp so that it is forced to go to the disk drive every time it writes a character. Redo the observations from the preceding question. You may need to reduce the number of iterations that are timed. Are the results quite different?

  6. Look up the rotational speed (in RPM) of the model of disk drive you are using. Convert this to revolutions per second. Does the number of blocks being written to the disk drive per second seem consistent with the disk drive's rotational speed? Follow up on this by checking whether the disk drive's write buffer is turned on, which can cause writes to the disk drive to go to semiconductor memory within the drive assembly, rather than to the actual magnetic disk. You can check this using the following command; be sure to use uppercase W:

    hdparm -W /dev/sda
    

    If you are not running in a root shell, you'll need to preface this with sudo to have permission to access the disk drive.

  7. Use the hdparm command with the -W0 or -W1 option to change the write buffer setting, and see what difference this makes in your experimental results with writer. (Beware: if your computer goes into sleep mode and wakes back up, this setting may revert to its original state.)

  8. Start one runner going on processor 0, and then in another shell window start one of your modified writers going on the same processor and observe their performance reports and the statistics from top and vmstat. How does each program's performance when they are run together compare with that when run alone? Does this tell you anything about the Linux scheduler, or about the potential utility of running multiple programs at the same time, even on uniprocessor systems? (You may choose to do this experiment with either write buffer setting, or with both.)

  9. Your results in the prior experiment may have been significantly influenced by the Linux scheduler's ability to preempt a running thread before its time slice is up, if another thread becomes runnable. To follow up on this, try repeating the experiment, but this time run the writer using "batch" scheduling, which you can specify using the -B option to schedtool. Note that the documentation for batch scheduling is out of date; the only impact it should have is that the batch thread (the writer) will never preempt a running thread before the running thread's timeslice ends. (There is one other effect of batch scheduling, which doesn't arise with the writer program, namely how the sched_yield system call works.)

  10. Given that I said the documentation is out of date, you might reasonably wonder how I know what batch scheduling actually does. Use the LXR Linux cross reference to look at the source file kernel/sched_fair.c and search for "batch". You might want to quote a relevant excerpt of a few lines in your lab report.


Instructor: Max Hailperin