EDU.oswego.cs.dl.util.concurrent.misc
Class SynchronizationTimer

java.lang.Object
  |
  +--EDU.oswego.cs.dl.util.concurrent.misc.SynchronizationTimer

public class SynchronizationTimer
extends java.lang.Object

This program records times for various fine-grained synchronization schemes, and provides some ways of measuring them over different context parameters.

Quick start:

  1. javac -d base of some CLASSPATH *.java
    You'll need Swing (JFC). (This program currently imports the javax.swing versions. You can edit imports to instead use other versions.)
  2. java EDU.oswego.cs.dl.util.concurrent.misc.SynchronizationTimer
  3. Click start. Clicking stop cancels the run. Cancellation can take a while when there are a lot of threads.
  4. For more explanation about tested classes, see Documentation for util.concurrent

Synchronization schemes are tested around implementations and subclasses of RNG, which is just a hacked random number generator class. Objects of this class have just enough state and require just enough computation to be reasonable minimal targets. (Additionally, random numbers are needed a lot in these kinds of time tests, so basing them on objects that produce random numbers is convenient.) Computation of each random number is padded a bit with an adjustable compute loop running a random number of times to avoid getting schedulers locked into uninteresting patterns.

Each iteration of each test ultimately somehow calls the random number generation method of an RNG. The time listed is the average time it took to do one iteration, in microseconds. These are just based on wallclock time (System.currentTimeMillis()). Thread construction time is NOT included in these times. In tests with many threads, construction and other bookkeeping can take longer than the tests themselves.

Results are listed in a table, and optionally printed on standard output. You can redirect standard output to save to a file.

The total amount of ``real'' computation reported in each cell is the same. Thus, the unobtainably ideal pattern of results would be for every cell of the table to be the same (and very small).

A thread pool (PooledExecutor) is used to manage the threads used in test runs. The current number of active threads is indicated in the panel. It should normally be at most three plus the number of threads used in the indicated test column (there are at most three overhead threads per run), although it may transiently climb, and is larger in those tests that generate their own internal threads (for example ThreadedExceutor). If the indicated size fails to return to zero within about 10 seconds of either hitting stop or the end of a run, you may have a problem with interruption handling on your Java VM.

This program cannot tell you how busy your computer is while running tests. You can run a utility program (for example perfmeter or top on unix) alongside this program to find out.

A number of control parameters can be changed at any time. Most combinations of parameter settings create contexts that are completely unrepresentative of those seen in practical applications. However, they can be set to provide rough analogs of real applications, and the results used as rough guesses about performance impact. Also, do not be too upset about slow performance on tests representing situations that would never occur in practice.

You can control parameters by clicking any of the following, at any time. (You can even change parameters while tests are running, in which case they will take effect as soon as possible. Most controls momentarily stall while test objects and threads are being constructed, to avoid inconsistencies during test runs.)

Number of threads
Controls concurrency. The indicated number of threads are started simultaneously and then waited out.
Contention.
Percent sharing among threads. Zero percent means that each thread has its own RNG object, so there is no interference among threads. The zero percent case thus shows the cost of synchronization mechanics that happen to never be needed. 100 percent sharing means that all threads call methods on the same object, so each thread will have to wait until the RNG objects are not being used by others. In between is in between: Only the given percentage of calls are made to shared RNG objects; others are to unshared. Contention in classes that use Channels works slightly differently: The Channels are shared, not the base RNG objects. (Another way of looking at it is that tests demonstrate effects of multiple producers and consumers on the same channel.)
Classes
You can choose to only test the indicated classes. You can probably figure out how to add more classes to run yourself.
Calls per thread per test
Specifies number of iterations per thread per test. The listed times are averages over these iterations. The default seems to provide precise enough values for informal testing purposes. You should expect to see a fair amount of variation across repeated runs. If you get zeroes printed in any cell, this means that the test ran too fast to measure in milleconds, so you should increase the iterations value.
Computations per call
Specifies length of each call by setting an internal looping parameter inside each RNG object. Shorter calls lead to shorter times between synchronization measures. Longer calls, along with high contention can be used to force timeouts to occur.
Iterations per barrier.
Specifies the number of iterations performed by each thread until a synchronization barrier is forced with other threads, forcing it to wait for other threads to reach the same number of iterations. This controls the amount of interaction (versus contention) among threads. Setting to a value greater than the number of iterations per test effectively disables barriers.
Threads per barrier
Specifies how many threads are forced to synchronize at each barrier point. Greater numbers cause more threads to wait for each other at barriers. Setting to 1 means that a thread only has to wait for itself, which means not to wait at all.
Lock mode
For classes that support it, this controls whether mutual exclusion waits are done via standard blocking synchronization, or a loop repeatedly calling a timed wait.
Producer mode
For classes that support it, this controls whether producers perform blocking puts versus loops repeatedly calling offer.
Consumer mode
For classes that support it, this controls whether consumers perform blocking takes versus loops repeatedly calling poll.
Timeouts
Specifies the duration of timeouts used in timeout mode. A value of zero results in pure spin-loops.
Producer/consumer rates.
For tests involving producer/consumer pairs, this controls whether the producer is much faster, about the same speed, or much slower than the consumer. This is implemented by having the producer do all, half, or none of the actual calls to update, in addition to adding elements to channel.
Buffer capacity
For tests involving finite capacity buffers, this controls maximum buffer size.

To scaffold all this, the RNG class is defined in layers. Each RNG has internal non-public methods that do the actual computation, and public methods that call the internal ones. The particular classes run in tests might change over time, but currently includes the following general kinds:

Using built-in synchronization
Versions of RNG classes that use (or don't use) synchronized methods and/or blocks. Also some tests of simple SynchronizedVariables. Tests that would not be thread-safe are not run when there is more than one thread and non-zero contention.
Using Sync classes as locks
Classes protecting public methods via Semaphores, mutexes, etc. In each case, the outer public methods delegate actions to another RNG object, surrounded by acquire/release/etc. The class called SDelegated does this using builtin synchronization rather than Sync locks so might be a useful comparison.
Using Channels
These classes work a little bit differently than the others. Each test arranges that half of the threads behave as producers, and half as consumers. Each test iteration puts/takes an RNG object through a channel before or after executing its update method. When the number of threads is one, each producer simply consumers its own object. Some Channels (notably SynchronousChannels) cannot be used with only one thread, in which case the test is skipped.
Using Executors
These classes arrange for each RNG update to occur as an executable command. Each test iteration passes a command to an Executor, which eventually executes it. Execution is overlapped: Each iteration starts a new command, and then waits for the previous command to complete.

The test code is ugly; it has just evolved over the years. Sorry.


Constructor Summary
SynchronizationTimer()
           
 
Method Summary
static void main(java.lang.String[] args)
          Start up this application
 
Methods inherited from class java.lang.Object
equals, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
 

Constructor Detail

SynchronizationTimer

public SynchronizationTimer()
Method Detail

main

public static void main(java.lang.String[] args)
Start up this application