|
This guide describes how to
use Sun Java Real-Time System (Java
RTS) to quickly and easily achieve determinism in a
Java RTS application by eliminating the usual sources of jitter.
This document is intended for programmers with minimum
knowledge of the Java programming
language and environment, as well as the Solaris
Operating System (Solaris OS).
The Practical Introduction
to Achieving Determinism Annex
document contains complementary, more detailed information about the
elements described in this document.
Contents
[Readme File]
[Installation Guide]
[Release Notes]
[Compilation Guide]
[Garbage Collection Guide]
[Technical Information]
[Java RTS DTrace Provider]
Introduction
The purpose of this document is to get you started, quickly
and easily, using Java RTS to achieve determinism in a real-time
application.
The main characteristic of a real-time application is that the
system is subject to real-time constraints. Response times must be
deterministic, in other words, guaranteed and predictable. The Sun Java
Real-Time System guarantees determinism by ensuring that critical code
is executed at the highest priority, even higher than the garbage
collector (GC) if needed. In addition, the Java RTS implementation
allows any Java Platform, Standard
Edition (Java SE) application to run at the same time as a Java RTS
application without affecting the determinism of the real-time
application.
The first Java Specification Request (JSR) issued by the Java
Community ProcessSM (JCP) was JSR 1: the Real-Time
Specification for Java (RTSJ). The Sun Java Real-Time System (Java
RTS) is Sun's implementation of that specification.
We define non-real-time and real-time requirements for an
application as follows:
-
A non-real-time application
has no time constraint requirements.
-
In a real-time application,
response times must be deterministic, in other words, guaranteed and
predictable.
-
In a soft real-time
application, missing one deadline is not critical.
-
In a hard real-time application, time
constraints must always be met.
Determinism can be expressed in terms of jitter, which
is central to measuring the deterministic behavior of a program. Jitter
measures the variation in a particular response time. An ideal value for
jitter would be zero, indicating perfect determinism. More
realistically, the aim of real-time processing is to reduce jitter to
an acceptable level. Moreover, any jitter that occurs must be bounded,
that is, we must guarantee that the jitter remains within known bounds.
This document is provided with a "Getting Started" package
(GettingStarted.zip), which
contains the example programs that support the explanations and
comments. They are very simple: they iterate a predefined sequence of
calls to perform calculation, memory allocation, and also pause time. We
measure for each iteration the wall-clock-elapsed time. The jitter
is defined here as the difference between the best and the
worst execution time of the set of iterations when running the programs
of the package.
This document has four parts:
-
The first part presents the
usual
sources of jitter that can occur in a Java program. See the section Usual Sources of Jitter.
-
The core of the
document describes a set of scenarios
executed by the script file contained in the "Getting
Started" package. Each scenario shows a use case of
running the example programs using either a vanilla VM
(such as the Java HotSpot
virtual machine) or the Java RTS VM.
The purpose of the scenarios is to show specific situations or
ways to achieve determinism using Java RTS. Each scenario includes
an output example, comments, and an interpretation of the results.
See the section Scenarios for the Example
Programs.
-
Then the document describes
how to set up the "Getting
Started" package and how to run the accompanying script file
that runs the scenarios. See the
section Running the Provided
Scenarios.
-
Finally, the Conclusion section
summarizes the various situations and ways to achieve determinism
that were described in the scenario section.
The "Getting Started" package contains all the
elements necessary for all levels of user interest:
-
Example programs that
demonstrate non-deterministic behavior, as well as deterministic
behavior, with and without garbage collection. The source code and the
bytecode for the example programs are included.
-
Several predefined scenarios
of all the example programs, showing the effect of configuration on the
determinism of the programs. For example, we configure the priorities
of the various threads, the type of compilation, the load on CPU, the
load on memory allocation and garbage collection, the length of time
a scheduled thread pauses to allow other threads to execute, the use of the
Real-Time Garbage Collector, amount of memory reserved for activity of
critical threads, and so forth.
-
The script file that automatically runs all the scenarios.
The example programs of the package are called
NonDeterministic, Deterministic, and GCDeterministic:
-
The first program
is a non-real-time program and executes in a non-deterministic way.
-
The
second program is a real-time Java RTS program. The variation in
execution time is practically zero, thus making the program predictable, that is,
deterministic. This program does
not consume memory in its steady state, and garbage collection never
comes into play.
-
The third program brings the Java RTS Real-Time Garbage
Collector (RTGC) into the picture to show how to combine
real-time
priorities of threads and configure the RTGC to maintain determinism.
Provided with the programs is a script that runs several
scenarios by setting various program and Java RTS VM command-line
parameters, in various configurations. You
can run this script as-is, with no changes, to see how each
configuration affects the resulting level of
determinism. In addition, you can modify the script, tune the
parameters, and even make changes to the programs themselves. For
example, you can test your own code for determinism by hooking it into
the example programs.
For more details about the
programs (processing, input, output), the script file,
and executing your own code, see the
Practical Introduction
to Achieving Determinism Annex.
[Contents]
Usual Sources of Jitter
The determinism of an application depends most heavily on
reducing and controlling jitter. To accomplish this, you must first be
aware of what conditions can cause jitter. Jitter can be caused by many
activities taking place in the system, including various VM internal
activities. This section discusses the most common sources of jitter
and how to deal with them.
Class Loading and Static
Initialization
The Java VM normally loads a class when the program first
tries to make an instance of the class, or when the program tries to
use a static method or variable of the class; when the class is loaded,
its static variables are initialized. This activity, of course, takes
execution time and can interfere with the execution of any code that is
critical for the real-time application. Since you cannot predict when
classes will be loaded, you cannot predict when this jitter will occur,
nor whether the amount of jitter is acceptable.
However, you can specify a list of classes to be preloaded
when the application is started, as well as a list of classes to be
preinitialized. In addition, you can request the Java RTS to generate
these lists for you. See the Java
RTS Compilation Guide for details.
Compilation
Java RTS currently supports two compilation modes:
Just-in-Time (JIT) compilation and Initialization-Time Compilation
(ITC).
JIT is the original compilation mode for the Java HotSpot
virtual machine, where compilation of a
method is triggered when certain internal counters reach specified
limits. In a non-real-time environment, this mode is designed to
trigger compilation at optimal times. However, this mode can cause
jitter in a real-time environment.
In the ITC mode, certain specified classes are preloaded and
marked for ITC compilation. When such a class is initialized, some or
all of its methods are compiled at that time. You specify
these classes and methods in a list that is passed as an argument at
run-time. You can also ask Java RTS to create the list for you.
By specifying that methods in critical code sections are to
be compiled at initialization, you can reduce or eliminate jitter
caused by run-time (JIT) compilation.
For detailed information, see the Java RTS Compilation Guide.
Garbage Collection
Java RTS supports two garbage collectors: the non-real-time
serial garbage collector, and the Real-Time Garbage Collector (RTGC).
When the non-real-time garbage collection occurs, the application
processes are suspended.
However, if the RTGC is used, it can be configured to execute
in a deterministic manner, that is, critical threads running at a
higher level of priority will not be interrupted by garbage collection.
The third example program illustrates the use of run-time parameters to
control when garbage collection can occur, thereby putting more control
over GC-caused jitter.
See the Java RTS
Garbage Collection Guide for a detailed discussion.
[Contents]
Scenarios for the
Example Programs
This section describes the scenarios for the three example
programs, states their purpose, and comments on the results.
As mentioned above, the three example programs -
NonDeterministic, Deterministic, and GCDeterministic - are intended to
show various sources of jitter, and how Java RTS avoids these jitter
pitfalls, using only instances
of the javax.realtime.RealtimeThread class (RTT),
the ITC compilation system, and the Java RTS
Real-Time Garbage Collector (RTGC).
The NoHeapRealtimeThread type of thread, dedicated to hard real-time, is
not used in these examples.
However, we show that hard real-time can be achieved
using RTTs running at critical priority (critical RTT),
correctly configuring the RTGC, and using ITC compilation.
Scenarios involving the NonDeterministic program are
intended to show failure of achieving determinism with a vanilla
VM. In those scenarios we put the vanilla VM in a situation where class
loading and class
initialization jitter is avoided. This is done by executing preliminary
stress with
the Fibonacci and GarbageProducer classes in the
static initialization code of the NonDeterministic program. This guarantees
that all classes are already preloaded and initialized while the
program is in its steady state. For the Deterministic and
GCDeterministic
programs, we use the recommended way of specifying preloading and
preinitialization: we build
preload and preinitialization lists that we provide at start-up of the
Java RTS VM, such that all classes used are loaded and initialized
before the program starts. See the Java
RTS Compilation Guide for more details.
The example programs supporting the scenarios use two types of threads:
Bench thread: The instance of thread on which we do
determinism measurements. The bench threads will be instances of
java.lang.Thread (JLT) in the NonDeterministic scenarios, and
instances of javax.realtime.RealtimeThread (RTT) in the
Deterministic and GCDeterministic scenarios. For each scenario there
is exactly one bench thread.
Stress thread: The instance of thread(s) that run in the program,
in addition to the bench thread, to consume CPU time and memory
in order to stress the system and try to break determinism.
The stress threads will be either JLT or RTT,
depending on the scenario. There can be at most two stress threads running in the scenarios:
one JLT and one RTT. In some scenarios there is only one JLT stress thread running,
and in others only one RTT stress thread.
For all scenarios, the bench and stress threads are described,
together with their type (RTT or JLT).
Be sure to refer to the
Practical Introduction
to Achieving Determinism Annex
for more detailed explanations about the programs.
Purpose of the Scenarios
Several of the scenarios use processor sets to take advantage
of the machine architecture, and to make sure that the selected CPUs in
the processor set are isolated from any other activity on the machine.
However, you can use the script as-is to run the scenarios on a
single-processor machine, even if
some of them, mainly those for the GCDeterministic program, need to
have a four-CPU machine. If you have a single-CPU machine, just remove
the psrset commands inside the script file. Notice that doing
so with the GCDeterministic scenarios will affect deterministic
behavior.
The scenarios described here are those that are run when using
the script file provided within the "Getting Started" package.
You can play with the scenarios by changing the input
parameters inside the script file and by modifying the contents of the
programs. The impact of those changes will help you observe and
understand real-time behavior, but is not mandatory. For an explanation
on how to make these changes, see the section Running the Script File (in this
document)
and the section Testing
Determinism with Your Own Code (in the Practical Introduction
to Achieving Determinism Annex).
Each program and dedicated set of scenarios fulfills several
objectives:
-
NonDeterministic scenarios show that you cannot
obtain deterministic behavior with instances of the standard java.lang.Thread
class (JLT), whether or not garbage collection (GC) activity is
necessary. The output figures are, however, clearer regarding
determinism failure when GC activity happens, especially on a multi-CPU
machine. The 7 scenarios run on a vanilla Java VM. The last 3 of
the scenarios stress GC activity.
-
Deterministic scenarios show that using RTTs will
immediately provide deterministic behavior in the normal case, where
all that
is necessary is to change JLTs to RTTs. In addition, these scenarios
show that the best deterministic behavior will be achieved using the
ITC compilation system. These scenarios also show the behavior of RTTs
regarding scheduling and the impact of priority. All of the scenarios
are free of any GC activity.
-
GCDeterministic scenarios deal with the case where
memory must be recycled, and this puts RTGC on the scene. When there
are
several real-time threads running together, and when garbage collection
becomes necessary, the situation becomes more complex, and these
scenarios illustrate some typical situations where both soft RTT and
critical RTTs exist together. There are 12
GCDeterministic scenarios:
-
The first 6 scenarios illustrate how to ensure
determinism with critical RTT and RTGC activity. They show that
critical RTTs are never interrupted by soft RTTs or RTGC threads,
except when the critical threads consume so much CPU that RTGC cannot get
enough free CPU to recycle memory. The last scenarios show that
one solution is to allow the RTGC to run
for enough time on a free CPU.
Most scenarios here are deterministic. Determinism is broken
only when a critical RTT cannot allocate any memory because
the RTGC did not recycle it in time.
The first 4 of these scenarios
force the run on only one processor that is not
interruptible. The last 2 use a processor set containing 2
processors. If you run the scenarios with the script file provided,
make sure that you run them on a multiprocessor machine (more
than 2).
-
The last 6 scenarios are dedicated to soft real-time
and show how we achieved determinism with soft RTTs. We start with a
simple situation where the priority level can provide determinism.
We then proceed, with more complicated configurations, to show how
to maintain determinism by setting the pause time and letting the auto-tuning
capacity of the
RTGC adapt to the situation. All these scenarios are to be run on a
single processor (a processor set with one non-interruptible processor
is used).
[Contents]
Analyzing the Output of the Scenarios
All the output is described in detail in the Output from the Example
Programs section of the Practical Introduction to Achieving Determinism
Annex. However, we describe here a part of the output
that is used for interpreting the results. The time unit of the
recorded values is the microsecond (except for the pause time,
which is in milliseconds).
Note: The scenario results that are shown in this document
were obtained on a Sun Fire
X4100 M2, with 4 CPUs, and with Solaris 5.10 OS U3.
The output includes the "noticeable deviations," that is,
loops whose execution time differs from the previous iteration
by more than 500 microseconds. Note that, for scenarios that are
considered deterministic, this output does not appear.
Here is an example:
====================================
Noticeable deviations:
====================================
For loop 75, execution time: 12545, deviation: -522
For loop 202, execution time: 13078, deviation: 575
For loop 330, execution time: 13080, deviation: 555
The above example shows that the execution times in loops 75, 202, and 330
deviated more than 500 microseconds from the preceding loop.
For all scenarios we provide a summary of the output results:
===================================== Summary of results: ===================================== Mean execution time: 377 Mean delta to previous: 130 Best execution time: 308 Worst execution time: 160331 Most frequent execution time: 313, occurred 345 times Execution time jitter: 160023 Standard deviation: 2886.587011790559
This summary shows for this given run:
-
The mean execution time for all the iterations.
-
The mean deviation value between two consecutive
iterations.
-
The best and the worst execution times observed.
-
The most frequent execution time value observed, together with
the number of occurrences. The most frequent value, although not
a statistical measure, indicates whether the run was stable in
the measurements performed on the bench thread. If the number of
occurrences is high, this means that were a lot of small
deviations between the measurements. The higher the number of
occurrences of the most frequent value, the more meaningful is the "mean
execution time" value.
-
The jitter measured for the whole run.
-
The standard deviation found after the last iteration.
For a deterministic scenario, the results should be similar to
the following:
-
The best and the worst execution time should be similar.
-
Jitter value should be in the hundreds of microseconds.
-
Standard deviation should be in the tens of microseconds.
-
Mean deviation value ("mean delta to previous") should be
in the low tens of microseconds.
[Contents]
NonDeterministic Scenarios
The script file executes 7 runs with a vanilla VM
such as HotSpot, or any VM that conforms to the Real-time
Specifications for Java, such as Java RTS. The first four
runs avoid bringing
garbage collection into play, while the rest produce some garbage
that must be recycled by the garbage
collector. All these scenarios run in a processor set with one CPU.
The script executes these scenarios with a vanilla VM. Separate
runs with Java RTS provided similar results, and these results
are reported also.
-
Run1: Processor set with 1 non-interruptible CPU, 1
bench JLT running at java max priority, 1 stress JLT running at java
min priority, no GC activity, 5000 iterations measured.
Output result
excerpt:
==========================================
Noticeable deviations:
==========================================
3 deviations more than 500 microseconds
==========================================
Summary of results:
==========================================
Mean execution time: 377
Mean delta to previous: 130
Best execution time: 308
Worst execution time: 160331
Most frequent execution: 313
Execution time jitter: 160023
Standard deviation: 2886.587011790559
==========================================
Comments:
This is a very good situation for
avoiding outliers. There is no memory load, and execution time
is very small (no time-sharing switch). Despite this, we
observe 3 very big outliers. Otherwise the collected
executions are almost always the same, and there are very few
deviations from the reference value of more than 5 microseconds.
Java RTS results are quite similar but more stable and more fixed
in the execution time.
Interpretation:
Determinism cannot be achieved with such a simple
Java program, even without any memory consumption.
-
Run2: Same as Run1, but the two threads have the
same java normal priority, and the execution is longer (around
time-sharing switch time); 1000 iterations.
Output result
excerpt:
========================================== Noticeable deviations: ========================================== 120 deviations more than 500 microseconds worst deviation to ref: 579944 worst deviation to mean: 569132 many bad deviations ========================================== Summary of results: ========================================== Mean execution time: 23333 Mean delta to previous: 21689 Best execution time: 12356 Worst execution time: 592465 Most frequent execution: 12521 Execution time jitter: 580109 Standard deviation: 37407.24853580082 ==========================================
Comments:
If execution time is longer than the
time-sharing switch time of the system, its effect immediately shows
up. The two threads are scheduled intermittently around 100 times. That
is the reason for these 120 outliers. Many more outliers occur
and are really bad. There is a lot of variation in the execution time.
Most of the deviations from the reference value are in the 40-200
microsecond range.
Java RTS results are quite similar but more stable and fixed in the
execution time.
Interpretation:
Execution time of the JLTs has a bad influence on the determinism since
the JLTs can be rescheduled one after the other more frequently by the
VM.
-
Run3: Same as Run2, but the bench thread is run with
maximum priority and the stress thread with minimum priority, trying to
take advantage of priorities in the standard Java.
Output result
excerpt:
========================================== Noticeable deviations: ========================================== 46 deviations more than 500 microseconds 41 deviations more than exec time worst deviation to ref: 160028 worst deviation to mean: 157047 most deviations are above 15 milliseconds ========================================== Summary of results: ========================================== Mean execution time: 15485 Mean delta to previous: 6012 Best execution time: 12281 Worst execution time: 172532 Most frequent execution: 12504 Execution time jitter: 160251 Standard deviation: 16114.78239520482 ==========================================
Comments:
Compared to Run2, there are fewer
outliers and they are shorter, but the situation has not changed
overall. There is no stabilization of the behavior, and outliers still
occur at the end of the run. Otherwise most of the deviations are in
the 20-200 microsecond range. Java RTS results are very
similar.
Interpretation:
Setting the priority of the bench thread to the
maximum and the priority of the stress thread to a minimum does not
bring fundamental progress. The scheduling of a JLT is based on
time-sharing
and even a high-priority thread can be preempted at any time by the VM
or another thread.
-
Run4: A processor set with only 1 CPU that is not
interruptible, only 1 bench JLT thread running at max priority,
execution time around time-sharing switch time, 1000 iterations.
Output result
excerpt:
========================================== Noticeable deviations: ========================================== 8 deviations more than 500 microseconds worst deviation to ref: 804 worst deviation to mean: 771 all deviations are less than 1 millisecond ========================================== Summary of results: ========================================== Mean execution time: 12505 Mean delta to previous: 102 Best execution time: 12361 Worst execution time: 13276 Most frequent execution: 12472 Execution time jitter: 915 Standard deviation: 100.95696498514157 ==========================================
Comments:
There are only 8 deviations of more than
500 microseconds, but this is the best situation that can be imagined
for the VM. The jitter due to JIT is not visible here because JIT
occurs in the first iteration, since we increased the number of inner
loops to get the expected execution time. Thus JIT occurs almost
immediately. You could lower the number of inner loops and increase the
number of stress class iterations to observe in the results that the
JIT happens much later in the run, causing a big outlier in the
results. Deviations are much smaller and the mean value seems
relevant. Most of the deviations are under 200 microseconds. Java RTS
shows a very similar result, but with smaller deviations between
execution times.
Interpretation:
In the best situation that be imagined for the VM (an
application with a single JLT running max priority, only basic
calculation without any garbage produced), correct behavior is almost
reached (but still 8 deviations of more than 500 microseconds). But is
such an application useful for real-time purposes?
-
Run5: Same as Run2, with the addition of garbage
production by the stress JLT.
Output result
excerpt:
========================================== Noticeable deviations: ========================================== 168 deviations more than 500 microseconds 153 deviations more than exec time worst deviation to ref: 550124 worst deviation to mean: 523355 all deviations above 10 milliseconds, many of them more than 100 milliseconds ========================================== Summary of results: ========================================== Mean execution time: 39130 Mean delta to previous: 51474 Best execution time: 12359 Worst execution time: 562485 Most frequent execution: 12361 Execution time jitter: 550126 Standard deviation: 71731.16277340992 ==========================================
Comments:
The number of outliers has dramatically
increased, the deviations are large, and most of the consecutive execution
times show a difference of about 200 microseconds. The price to pay for
GC is really heavy regarding determinism. Java RTS presents
very similar results with JLTs.
Interpretation:
Compared to Run2, the addition of a second
stress JLT producing garbage makes the picture even worse. You can see
in the "summary of results" section of the output that the best and
most frequent execution times are comparable, but the worst execution
time, the jitter, and the standard deviation have increased
dramatically. Any time the GC needs to recycle memory for the stress
thread to continue its job, both the bench and stress threads are
stopped, and this causes a lot of outliers. This occurs even in the
last loops of the run. When GC comes into the picture, there is clearly
no determinism.
-
Run6: Same as Run5, with bench thread running at maximum
and stress thread running at minimum priority.
Output result
excerpt:
========================================== Noticeable deviations: ========================================== 44 deviations more than 500 microseconds 34 deviations more than exec time worst deviation to ref: 240107 worst deviation to mean: 236052 most deviations above 10 milliseconds, many of them more than 100 milliseconds ========================================== Summary of results: ========================================== Mean execution time: 16419 Mean delta to previous: 7755 Best execution time: 12285 Worst execution time: 252471 Most frequent execution: 12364 Execution time jitter: 240186 Standard deviation: 23204.068886142697 ==========================================
Comments:
Deviations are still large, as shown by the 44 outliers due to GC
activity stopping the bench thread. This is also shown by the jitter and
standard deviation
values. However, the run is more stable. Deviation between each
iteration is about 200 microseconds.
Java RTS presents very similar results for JLTs.
Interpretation:
Changing the priority of the bench thread
to the maximum does not change anything. The bench thread is still
preempted by the GC when recycling memory.
-
Run7: Same as Run6, but where the stress thread does not
exist. Only the bench thread is running, and it runs at max priority and produces
garbage.
Output result
excerpt:
========================================== Noticeable deviations: ========================================== 174 deviations more than 500 microseconds 34 deviations more than exec time worst deviation to ref: 13250 worst deviation to mean: 11077 most deviations above 10 milliseconds, all below 15 milliseconds ========================================== Summary of results: ========================================== Mean execution time: 12229 Mean delta to previous: 4360 Best execution time: 9662 Worst execution time: 23306 Most frequent execution: 10056 Execution time jitter: 13644 Standard deviation: 4691.950409349416 ==========================================
Comments:
Results are much better but still not sufficient. There are 44 outliers
due to GC activity stopping the bench thread. Deviations between
consecutive execution times are larger (about 300 microseconds).
Even when there is only one thread
running, and this one producing garbage, determinism is largely broken
by GC activity when recycling memory needed for the bench thread. This
happens until the end the run. Java RTS shows very similar results
on JLT threads.
Interpretation:
When memory is being
recycled, the
bench thread is preempted by the GC activity, which takes a long time
and breaks potential determinism. This happens whenever memory becomes
too low, at any time.
All these scenarios show that you cannot achieve true
determinism with a vanilla VM. The separate runs with Java
RTS are very similar to those with
the vanilla VM in terms of throughput and behavior regarding JLTs.
[Contents]
Deterministic Scenarios
The Deterministic program can only be run on a VM that is
compliant with RTSJ. Run3 is expected to run on Java RTS
because it uses the ITC compilation system.
The first two of these scenarios do not use ITC
compilation and allow JIT compilation to occur. The Deterministic program uses
java.realtime.RealtimeThread (RTT), in addition to JLTs. There
is no memory consumption by the threads run by this program, so
there is no GC activity here. See the section Description of
the Example Programs in the Practical Introduction to Achieving Determinism Annex for
more information on what the Deterministic program does.
-
Run1: Runs an RTT as the bench thread running at maximum
real-time priority and a JLT running at minimum java priority (with
Fibonacci calculations only) as a stress thread. Uses a processor set
containing one processor.
There is a 1 millisecond pause time in the bench thread to allow
JIT compilation to occur; otherwise the bench thread would take
all the free CPU time and thus run in interpreted mode until termination.
There are 1000 iterations.
Output result
excerpt:
==========================================
Noticeable deviations:
==========================================
No Noticeable deviation
==========================================
Summary of results:
==========================================
Mean execution time: 1866
Mean delta to previous: 1
Best execution time: 1863
Worst execution time: 1951
Most frequent execution time: 1863, 872 occurrences
Execution time jitter: 88
Standard deviation: 13.211009688624728
===========================================
Comments:
As you can see in the output excerpt, the behavior is very
predictable. There is no deviation larger than 500 microseconds. Most
of the results are identical (1863 microseconds). This reference value
is found 872 times out of 1000 iterations. The worst deviation between
two consecutive iterations is only 88 microseconds, and happens only once.
The jitter figures (88 microseconds) and standard deviation figures
(13 microseconds) are excellent.
JIT compilation jitter is visible at the beginning of the run.
The first iteration is done in interpreted mode, then during the 1
millisecond pause JIT compilation is done asynchronously. Then the
following iterations are run progressively in JIT compilation mode
and the 40th iteration finally stabilizes at 1863 microseconds.
Execution time consists of 1 millisecond plus the computing time of
the Fibonacci numbers (863 microseconds). You can notice that the
JLT stress thread has no influence on the RTT bench thread.
This is expected, since the JLT is not executed at real-time priority.
The Deterministic program highly favors the JIT compilation
system since the executed code in the bench thread is always the same and
the closure of the method calls is completely traversed at each iteration.
In most applications this is not the case, and because of conditional
calls and logic program some method calls will happen much later,
thus creating unexpected JIT compilation jitter.
Interpretation:
It is very easy to get
deterministic behavior with Java RTS using RTTs! You just have to
replace java.lang.Thread
by javax.realtime.RealtimeThread
and you are done, if there is no memory consumption. However,
some real-time applications need to consume memory inside their time-critical
section, and this is why it is necessary to use either NHRTs or the RTGC.
We will not address the use of NHRTs in this document. For the use of the RTGC,
see the scenarios for the GCDeterministic program. We will see that
we can get controlled compilation and thus totally avoid compilation-related
jitter in Run3 using the ITC compilation system.
-
Run2: Same as Run1, with an additional RTT running at
minimum real-time priority (with Fibonacci
calculations only) as a stress thread. This run also pauses for 1 millisecond
in each iteration. There are 1000 iterations.
Output result
excerpt:
==========================================
Noticeable deviations:
==========================================
No Noticeable deviation
==========================================
Summary of results:
==========================================
Mean execution time: 1873
Mean delta to previous: 1
Best execution time: 1871
Worst execution time: 1939
Most frequent execution time: 1872
Execution time jitter: 68
Standard deviation: 8.305044483618005
==========================================
Comments:
Same behavior as for Run1. Introducing the second stress RTT does not
change anything. That was expected. This run shows that java RTS
follows the scheduling model of RTSJ. The stress RTT will execute only
when the bench RTT is pausing and JIT compilation has finished.
Interpretation:
Java RTS scheduling follows the Real-Time Specification for Java.
Thus neither the stress JLT nor the stress RTT
could disturb the execution of the bench RTT.
The bench RTT was scheduled to run every time it was not pausing.
Setting real-time priorities in Java RTS has a real effect
on the scheduling behavior of RTTs.
-
Run3: Same as Run2, but this time we will use the ITC
compilation system of Java RTS and observe the results. There are 5000
iterations. We use the precompile and preload lists.
Output result
excerpt:
==========================================
Noticeable deviations:
==========================================
No Noticeable deviation
==========================================
Summary of results:
==========================================
Mean execution time: 1865
Mean delta to previous: 1
Best execution time: 1860
Worst execution time: 1898
Most frequent execution time: 1865, 1550 occurrences
Execution time jitter: 38
Standard deviation: 4.1091109289879375
==========================================
Comments:
Using the ITC compilation system improves determinism. There are 1550
identical elapsed execution times out of 5000, and most of them
deviate less than 5 microseconds from the reference value.
The worst deviation is 33 microseconds.
For a vanilla VM and JIT, there were 1110
identical results, and small deviations were constant throughout the run.
However, the most important thing to notice is that the reference
value was almost immediately measured in the run (at iteration 2), and
stabilization of the run lasted from that point until its end.
Interpretation:
When there is no memory to
be recycled in the RTT critical section, ITC compilation solves the remaining
problems
for determinism by suppressing almost all the jitter sources that are not due to GC activity. The
ITC compilation system is the right way to get determinism when GC does
not happen. In the following scenarios for GCDeterministic, the ITC
compilation system is fully used (precompile and
preinitialization list).
The simplest way to get determinism with Java RTS is to
replace java.lang.Thread
with javax.realtime.RealtimeThread
when you do not consider GC constraints. Due to JIT compilation, there
could still be some jitter if some
classes are used late within the life of the RTTs.
The solution for this is to use the ITC compilation system, so that
you will have true determinism immediately after initialization of the application.
[Contents]
GCDeterministic Scenarios
The GCDeterministic program is to be run in all cases with
Java RTS only, on a multiprocessor machine, and with the
ITC compilation system activated for all classes and methods
(precompile and preinit lists both used). Processor sets are used to
illustrate the distributed execution of the RTGC. You can also run the
program without processor sets, but this will change the behavior
of the scenarios depending on the number of available CPUs on your machine
and the load.
See the
Java RTS Garbage Collection Guide for a description of the RTGC.
These scenarios show how to use the RTGC both
when hard real-time is necessary (critical bench RTT) and when soft
real-time is sufficient.
-
Run1: One bench RTT running at maximum real-time
priority (that is, a critical RTT) with no allocations. One stress RTT
with many allocations and running at minimum real-time priority. There
is no pause time neither for bench nor stress RTTs. The
run is forced to execute on only one non-interruptible processor.
There are 1000 iterations.
Output result
excerpt:
========================================== Noticeable deviations: ========================================== No Noticeable deviation
========================================== Summary of results: ========================================== Mean execution time: 214 Mean delta to previous: 0 Best execution time: 214 Worst execution time: 221 Most frequent execution: 214 Execution time jitter: 7 Standard deviation: 0.9244956625483084 ==========================================
Comments:
Hard real-time succeeds with this bench critical RTT. GC never happens
while the bench RTT is running, but only when it has finished and when
the stress RTT allocates too much memory. Notice that deviation to previous
is zero, jitter is 7 microseconds, and standard deviation is 1 microsecond.
These are excellent results (like Run3 of Deterministic scenarios).
Interpretation:
Again real-time priority and scheduling model of
Java RTS came in action in this run. The stress RTT allocating memory
could not start before the critical bench RTT terminated. Therefore it
could not be stopped for any memory recycling reason.
-
Run2: This is the same scenario as Run1, with the
bench RTT also allocating a lot of memory. Again there is no pause time
for the bench and stress RTTs. There are 1000 iterations.
Output result
excerpt:
========================================== Noticeable deviations: ========================================== 14 deviations more than 500 micros, due to GC 12 deviations more than exec time, due to GC worst deviation to ref 70354 worst deviation to mean 69543 deviations spread from 60000 to 70000 micros, most in 60000 micros range
========================================== Summary of results: ========================================== Mean execution time: 1879 Mean delta to previous: 1603 Best execution time: 1030 Worst execution time: 71422 Most frequent execution: 1068 Execution time jitter: 70392 Standard deviation: 7188.604628615975 ==========================================
Comments:
This is not a deterministic run. Jitter is large, as well as the standard
deviation. There are small variations in execution time (less than 40
microseconds). The bench thread is blocked 12 times out of 23 GC cycles.
These outliers occur because there is no pause in the bench RTT,
and therefore the RTGC cannot recycle memory.
The bench thread cannot allocate any more memory and is stopped.
Interpretation:
Failure to achieve a deterministic run
happened for two reasons: there was no free time left on the single CPU
to let the RTGC work; and there was no
RTGCCriticalReservedBytes threshold set for the bench RTT.
This is what we will set in the next scenario.
-
Run3: Same as Run2, but using the Java RTS
option RTGCCriticalReservedBytes of 10 megabytes. There is no
pause time within the iterations. This is executed in a processor
set containing one processor.
Output result
excerpt:
========================================== Summary of results: ========================================== Mean execution time: 1874 Mean delta to previous: 1587 Best execution time: 1034 Worst execution time: 68159 Most frequent execution: 1071 Execution time jitter: 67125 Standard deviation: 7111.513838677339 ==========================================
Comments:
The result is exactly the same as previous run. Nothing has changed.
For this reason we did not include the "noticeable deviations" output.
Interpretation:
Using the RTGCCriticalReservedBytes option is
the right way to allow the bench thread to continue to allocate memory when
needed. But again, since it is a critical thread, the bench RTT will
continue to run and will not let the RTGC work. Thus the critical
reserved bytes threshold will also be reached, and the bench thread is
stopped on allocation failure as in the previous run.
Critical threads must let the
RTGC run from time to time. The less CPU time it gets, the higher
the RTGCCriticalReservedBytes threshold must be.
Setting a pause time
in the bench thread is a solution in this case. This is what we
show in the next scenario.
-
Run4: Now we add a small pause time in the bench thread
execution. There are 1000 iterations.
Output result
excerpt:
========================================== Noticeable deviations: ========================================== only 1 deviation more than 500 microseconds
========================================== Summary of results: ========================================== Mean execution time: 5140 Mean delta to previous: 61 Best execution time: 4913 Worst execution time: 5551 Most frequent execution: 5091 Execution time jitter: 638 Standard deviation: 97.84451410510545 ==========================================
Comments:
There is only one deviation to previous that is more than 500 microseconds.
Moreover, this deviation is less than 500 microseconds from the reference value
(460 microseconds). This is a good determinism achievement.
Determinism is guaranteed, firstly by allowing a pause time in the bench thread to
give the RTGC some time to work, and secondly by specifying the
RTGCCriticalReservedBytes threshold
(sized against the allocation rate of the bench thread).
If we increase the pause time in the bench thread, the run will be even
more deterministic.
Interpretation:
This run shows the correct usage of the
RTGCCriticalReservedBytes option, which is very important for avoiding
the previous situation for critical threads when garbage is produced.
On a single-CPU machine, even with a very small pause time
in the bench thread, setting the RTGCCriticalReservedBytes
option allows the RTGC to make enough progress
to avoid a memory starvation.
On a multiprocessor machine, a pause time of zero will
still work if the allocation rate of the bench RTT is not too high.
This is because the RTGC will run on a
separate CPU and thus will be able to collect memory rapidly enough.
However, if the RTGCCriticalReservedBytes are consumed too fast, the critical
thread will be stopped again as in Run2 and Run3.
You can play with the pause
time parameter for the bench thread and the size of the
RTGCCriticalReservedBytes
memory buffer to find the minimal numbers to avoid stopping the bench
thread. The longer the pause time, the better determinism figures you
will get, which are already excellent for a minimum pause time of 4 milliseconds.
The value of the RTGCCriticalReservedBytes threshold depends on
several factors: the allocation rate, the CPU time remaining for the RTGC,
and the memory that can be freed by each RTGC cycle.
Run5: This is the same as Run3, but this time rather than
adding a pause time, we use a processor set with 2 CPUs.
There are 1000 iterations.
Output result
excerpt:
========================================== Noticeable deviations: ========================================== No noticeable deviation ========================================== Summary of results: ========================================== Mean execution time: 833 Mean delta to previous: 20 Best execution time: 692 Worst execution time: 1154 Most frequent execution: 796 Execution time jitter: 462 Standard deviation: 80.80933946083752 ==========================================
Comments:
The determinism is good. There is no deviation more than
500 microseconds. If the allocation
rate of the bench thread is not too heavy, the GC will be able to use the
second CPU to work and ensure determinism. This is the case here.
Interpretation: The RTGC was able to run on the second
CPU while the critical bench thread continued to run. In this case
the allocation rate of the bench thread was not too huge, and the RTGC
could recycle memory quickly enough to let the bench thread
continue allocating. In the next run we lower the allocation rate of
the bench thread.
-
Run6: Same as Run5, except that the heap load on the critical
bench thread is not heavy. There are 1000 iterations.
Output result
excerpt:
========================================== Noticeable deviations: ========================================== No noticeable deviation ========================================== Summary of results: ========================================== Mean execution time: 313 Mean delta to previous: 15 Best execution time: 279 Worst execution time: 483 Most frequent execution: 317 Execution time jitter: 204 Standard deviation: 18.33126612814147 ==========================================
Comments:
The results are even better than for Run5. The standard deviation
is divided by 4, jitter is divided by 2, and mean of delta between
consecutive iterations went from 20 to 15 microseconds, which is excellent.
Interpretation:
These results are
closer to those that you can achieve with a real application,
which would not stress the RTGC as much as in the previous run.
-
Run7: From this run to the last
scenario, we explore the tuning of the RTGC for soft real-time. So in all
these scenarios, all running RTT threads have a priority that is
less than the critical priority. For this first soft real-time scenario, we
only change the priority of the bench thread from critical to normal, and
observe the result. The run is executed in a single CPU processor set.
There are 1000 iterations.
Output result
excerpt:
========================================== Noticeable deviations: ========================================== No noticeable deviation ========================================== Summary of results: ========================================== Mean execution time: 214 Mean delta to previous: 0 Best execution time: 214 Worst execution time: 234 Most frequent execution: 214 Execution time jitter: 20 Standard deviation: 1.076301924411748 ==========================================
Comments:
The results are very similar to those of Run1. Only one deviation is
20 microseconds, with a few deviations between 1 and 7 micros. Standard
deviation is 1 microsecond. This is because, even with soft real-time,
the priority of the bench RTT is higher than the one of the stress RTT.
Therefore this run is very similar to Run1 because of the scheduling
model of RTSJ.
Interpretation:
The combined priority of the bench and stress
RTTs together with scheduling model of RTSJ made this run
deterministic. The stress RTT (the only one producing garbage) was
not able to start before the bench thread finished.
Thus the stress RTT could run alone on the single
CPU without being stopped by either the RTGC or the stress RTT activity.
-
Run8: Same as Run7, but the bench RTT is now
producing a lot of garbage to recycle. There are 1000 iterations.
Output result
excerpt:
========================================== Noticeable deviations: ========================================== 12 deviations more than 500 micros, due to GC worst deviation to ref: 58800 worst deviation to mean: 58101 deviations spread from 57000 to 59000 micros
========================================== Summary of results: ========================================== Mean execution time: 1759 Mean delta to previous: 1420 Best execution time: 998 Worst execution time: 59860 Most frequent execution: 1056 Execution time jitter: 58862 Standard deviation: 6351.758741005157 ==========================================
Comments:
Determinism has been somewhat lost, but except for the loops
where the RTGC had to come in action on the only available CPU, the run
was very stable. Deviations are small and less than 63 microseconds.
In any case, full determinism cannot be achieved because at some time the RTGC
has to collect garbage and can only do that by delaying the bench RTT.
There are only 12 deviations, and they are very regular even though the
allocation rate was heavy.
Interpretation:
Whenever the RTGC detects that memory is low, this means that it
did not get enough CPU time to recycle memory. Therefore, the priority of
the RTGC collector
threads is boosted to critical in order to be able to work and recycle
memory. This is done very regularly, and except for those outliers,
the elapsed execution time is very predictable.
-
Run9: We use again a critical reserved bytes size of
10 megabytes as in Run3. Otherwise the run is identical to Run8.
There are 1000 iterations.
Output result
excerpt:
========================================== Noticeable deviations: ========================================== 14 deviations more than 500 micros, due to GC, occurring from start to end of run worst deviation to ref: 51932 worst deviation to mean: 51202 all deviations spread in the 51000 to 52000 micros range
========================================== Summary of results: ========================================== Mean execution time: 1779 Mean delta to previous: 1465 Best execution time: 989 Worst execution time: 52981 Most frequent execution: 1049 Execution time jitter: 51992 Standard deviation: 6064.586535587456 ==========================================
Comments:
The behavior is exactly the same as in Run8. The
RTGCCriticalReservedBytes option does not provide any advantage
since the bench is not a critical thread, and so cannot take advantage
of this reserved memory. Again the thread is stopped when RTGC activity comes in the
picture.
Interpretation:
RTGCCriticalReservedBytes will only impact the behavior of the critical RTTs
that do some allocation.
-
Run10: We now allow the bench soft real-time RTT to
have a pause of 4 milliseconds in each iteration, expecting the RTGC to take
advantage and do the collection during the pause time.
There are 1000 iterations.
Output result
excerpt:
========================================== Noticeable deviations: ========================================== 23 deviations more than 500 micros, due to GC, occurring from start to end of run worst deviation to ref: 61581 worst deviation to mean: 60262 all deviations spread in the 50000 to 60000 micros range deviations stop after iteration 204 ========================================== Summary of results: ========================================== Mean execution time: 6381 Mean delta to previous: 2569 Best execution time: 5047 Worst execution time: 66643 Most frequent execution: 5062 Execution time jitter: 61596 Standard deviation: 8329.519161824699 ==========================================
Comments:
Trying to let RTGC do collection when the bench RTT is idle is a good idea.
What must be noticed here is that all deviations occur in the first 200
iterations, and then stop. The RTGC has been able to adapt to the
allocation situation of the program. The worst deviation after the
204th iteration is less than 300 microseconds.
Interpretation:
Even if this is not a true application, this illustrates at least the
auto-tuning feature of RTGC for the soft real-time. So again,
if you want to reach good determinism for soft real-time, remember
that putting some pause time in the bench RTT, thus freeing CPU time for the RTGC,
will help the RTGC auto-tune. This is not guaranteed, however, because
the stress RTT was also able to use this
pause time to run, and this degrades the auto-tuning capacity of the RTGC.
So let's apply this pause time principle to both running RTTs
in order to give the RTGC more chance to work.
-
Run11: Same as Run10, but we also pause the stress RTT for
4 milliseconds in each iteration. There are 1000 iterations.
Output result
excerpt:
========================================== Noticeable deviations: ========================================== 4 deviations more than 500 micros, due to GC, occurring at the beginning of the run worst deviation to ref: 59437 worst deviation to mean: 59197 all deviations spread in the 50000 to 60000 micros range last deviation happened at 169th iteration. ========================================== Summary of results: ========================================== Mean execution time: 4846 Mean delta to previous: 283 Best execution time: 4645 Worst execution time: 65099 Most frequent execution: 4706 Execution time jitter: 60454 Standard deviation: 2784.886518452266 ==========================================
Comments:
Auto-tuning of the RTGC happens now very rapidly, after 12 GC cycles. The RTGC
has been able to adapt to the situation and avoid any jitter on the
bench thread after the first 170 iterations. This is a second win for the soft
real-time thread.
Interpretation:
As we set also a pause time in the stress RTT, the RTGC is adapting much more
quickly. A good rule of thumb to get more deterministic behavior
is to ensure that the load is low enough so that your real-time
threads do not use 100% of the CPU power.
Again, this cannot be guaranteed and depends on the memory allocation
rate of the application.
-
Run12: Same as Run11, while using the NormalMinFreeBytes
option set to 10 megabytes. There are 1000 iterations.
Output result
excerpt:
========================================== Noticeable deviations: ========================================== No noticeable deviation ========================================== Summary of results: ========================================== Mean execution time: 5101 Mean delta to previous: 40 Best execution time: 4894 Worst execution time: 5339 Most frequent execution: 5075 Execution time jitter: 445 Standard deviation: 76.18042474301542 ==========================================
Comments:
We now get excellent results. The RTGC now starts more quickly (as soon
as the remaining free memory goes below 10 megabytes) because we
set the NormalMinFreeBytes threshold. Observing the summary of
results, you can see that the run is very deterministic, and the mean
to delta to previous is 40 microseconds. There is no deviation more than
300 microseconds.
Interpretation:
The RTGC was able to adapt more quickly because we forced it to start
sooner.
[Contents]
Conclusions from the Scenarios
The scenarios for each of the example programs have led to the
following set of conclusions:
-
The NonDeterministic program
showed that determinism cannot be achieved on a standard VM because of
the default scheduling policy of Java and garbage collection activity,
even putting the VM in the best possible situation regarding known
sources of jitter.
-
Running the Deterministic
program showed that determinism is immediately achieved using RTTs with
Java RTS when the application does depend on
memory being recycled.
The recommended way to fully control or avoid class
loading, initialization, and compilation jitter is to use properly the
ITC compilation system.
-
The GCDeterministic program showed that critical RTTs
cannot be interrupted by the RTGC.
To guarantee determinism with garbage collection, configure
the RTGCCriticalReservedBytes parameter to reserve some memory
for the critical RTTs that you want to behave deterministically.
A key point to the design of the application is to balance the number
of critical threads and the CPU usage so that the RTGC
will have free CPU cycles to work with. If you have too many critical RTTs,
or if they allocate too much memory or use too much CPU
time, determinism might be compromised.
The recommended practice is to use critical
RTTs for hard real-time constraints and RTTs with non-critical priority
for soft real-time.
For soft real-time RTT, the rule of thumb to achieve
determinism is to make sure that the RTGC gets some free time on an
available CPU in order to tune its activity based on the overall
allocation rate behavior. You can cause the RTGC to start earlier
by setting the NormalMinFreeBytes
threshold to something different from zero. In this way, the memory
recycling will be stabilized earlier. Again, the best value will be
obtained by observing the application behavior regarding memory
consumption.
[Contents]
Running the
Provided
Scenarios
This document is included in a package that allows
you to execute the scenarios on your own Java RTS installation. This
package consists of a jar file containing the Java bytecode of the
examples, the source of the examples, and a script file that runs
the scenarios exactly as described above. This section explains how to
use
this package. It is useful if you want to see how the scenarios behave
on your own machine. You will find more details on the programs
in the Practical Introduction
to Achieving Determinism Annex.
Setting
up the "Getting Started" Package
To run the examples of the "Getting Started" package on your Java Real-Time
System, you must either run as root
superuser or ensure you have the right privileges. Before trying to run
the programs, see the Java
RTS Installation Notes for information on correctly
setting the privileges.
The examples are provided both as jar archives and source
code. They are packed in the GettingStarted.zip
file. This compressed archive contains the following:
-
run-scenarios.sh shell script. This shell script
file will run all the scenarios, one after the other, in the same order
as they are described in this document. See the section Running the Script File.
-
GettingStarted.jar Java archive. This file contains
the bytecode for all the classes of the example programs. You can use
it as-is to run the programs. This jar file will be an input parameter
to the script file to run the NonDeterministic, Deterministic, and
GCDeterministic programs.
-
src directory containing the source code for the
example programs.
Before starting, change the current directory to your working
directory:
$ cd <working-dir>
Then perform the following steps:
-
Copy the GettingStarted.zip file to your working
directory.
-
Unzip the GettingStarted.zip file.
$ unzip GettingStarted.zip $ ls GettingStarted.jar run-scenarios.sh src
-
If you do not want to modify the source code and play with
the example programs now, you have finished the set-up. You can now
do either of the following:
You are free to modify the examples. Recompile the
modified Java source as described in the section below.
The example programs contain hooks where you can execute your
own code as a "stress class," in addition to or in place of the
stress classes used in the examples. This allows you to test your own Java code for
determinism. See the section
Testing Determinism With Your Own Code in the Practical Introduction
to Achieving Determinism Annex.
Compiling
the Programs
If you make changes to the programs or add your own code,
compile them with the following commands:
$cd <working-dir>
$<Java-RTS-install-dir>/SUNWrtjv/bin/javac \
-classpath src:<Java-RTS-install-dir>/SUNWrtjv/lib/rt2.jar src/*.java
The <Java-RTS-install-dir> variable is the
location of your Java RTS installation. This will compile all the
source code in the src directory and produce class files
that you can run manually as described below.
Running the Programs
To run the compiled program from the jar file, type the
following commands:
$cd <working-dir>
$<Java-RTS-install-dir>/SUNWrtjv/bin/java <java-vm-args> \
-classpath ' GettingStarted.jar | src ' \
-client \
<example-program> \
<bench-thread-parameters> <stress-thread-parameters> \
[<stress-thread-parameters>] [<verbosity-flag>]
The arguments are as follows:
-
<Java-RTS-install-dir> is the location of
your Java RTS installation.
-
<java-vm-args> are any options that you want
to set for this run of the VM, such as the Java RTS options for
invoking ITC compilation system or for tuning the Real-Time Garbage
Collector. See the contents of the provided script file and the
documentation provided with Java RTS for information about all the
options for Java RTS.
-
' GettingStarted.jar | src ' : Specifying GettingStarted.jar
will run the programs as-is, as they are found in the GettingStarted.jar
file. Specifying src will run the programs from the class
files that you may have modified and recompiled, as described in
the section Compiling the Programs
above.
-
<example-program> is one of NonDeterministic,
Deterministic, GCDeterministic.
-
<bench-thread-parameters> and <stress-thread-parameters> ,
as well as <verbosity-flag> , are described in the section Input Parameters for the
Example Programs in the Practical Introduction to Achieving Determinism Annex.
Be sure to refer to the Sun
Java RTS Release Notes for detailed information about running
Java RTS.
Running the Script File
The script file that is provided with the program jar file and
source code runs the scenarios exactly as they are described in this
document, and in the same order. See the section Scenarios for the Example
Programs above in this document. The script is a very simple Bourne
shell (sh) script that
executes the sequence of instructions for each scenario. The parameters
and the name of the output file are different for each scenario. Please
notice that the scenarios are expected to run on a multiprocessor
machine and use processor sets.
For more information about using processor sets, see the section
on using multiprocessors in the
Java RTS Technical Information document, as well as the article
Solaris Processor Sets Made Easy.
To run the script file, do the following:
$cd <working-dir>
$./run-scenarios.sh <script-parameters>
The format of the <script-parameters> is as follows:
<vanilla-java-command> <javaRTS-java-command> \
<verbose-flag> <getting-started-jar> <optional-vm-args>
The meaning of the arguments:
-
<vanilla-java-command>: Path and binary
java command for the vanilla Java VM to use.
-
<javaRTS-java-command>: Path and binary
java command for the Java RTS VM to use.
-
<verbose-flag>: The value "verbose"
will produce detailed output from the programs. Any other value
produces a summary.
-
<getting-started-jar>: Path and filename of
the GettingStarted.jar file.
-
<optional-vm-args>: Arguments that you may
want to pass to the VMs used by the script file. These arguments are
passed to all scenarios, and thus are passed to both vanilla Java VM
and Java RTS VM. For this reason, any Java RTS VM specific argument
that is passed will cause the scenarios for the vanilla Java VM to fail
since they will not be recognized.
Here is an example of the script file execution:
$./run-scenarios.sh java /opt/SUNWrtjv/bin/java verbose
<full-path-to-working-dir>/GettingStarted.jar \
-XX:+RTGCPrintStatistics
Each run of a scenario within the script creates an output file with
a name that reflects the parameters of the scenario, the run command,
and the scenario number.
You can also modify the script file, run it, and observe the
differences. The header of the script file explains how to modify it.
[Contents]
Conclusion
This document has presented, in various levels of detail, a
set of example programs that you can run in order to see first-hand how
to achieve determinism in a real-time program by using Java RTS.
Real-time constraints imply that response times must be guaranteed and
predictable, in other words, deterministic. We define determinism in
terms of jitter, that is, the variation in execution time.
Provided with the document is a complete package containing
the following:
-
A jar file containing the
bytecode for all the example programs.
-
A script file to run several
predefined scenarios for each program.
-
The source code for all the programs.
Having all this data means that you can test the programs on
several levels:
-
Run the script as-is, with
no changes, and examine the output.
-
Make changes to the script,
run it, and observe the differences. See the header of the script file
for instructions.
-
Hook in your own code to be
executed in one of the program threads, and then see how that code
affects the results. See the
Practical Introduction to Achieving Determinism Annex for more information.
-
Make changes to the source code of the programs, test it,
and run it. See the
Practical Introduction to Achieving Determinism Annex for more information.
The scenarios were created with the intention of demonstrating
various combinations of the major parameters that can play a part in
how the programs behave: type of VM (vanilla HotSpot vs Java RTS), type
of thread (JLT vs RTT), level of real-time thread priority (soft vs
hard/critical), thread priority value (absolute, as well as relative to
the priorities of other threads, including the RTGC), compilation
scheme (JIT vs ITC), time-sharing with other threads, CPU consumption,
memory consumption, amount of memory reserved for critical and
non-critical RTT threads,
garbage collection (vanilla Java VM garbage collector vs RTGC), pause
time, total number
of CPUs that the RTGC can use for all its running threads,
multiprocessor environment, and processor sets.
Some of the Java RTS VM flags have been used for the
scenarios, and some of these are mentioned in the description of the
scenarios. All of these flags are described in the Java RTS
documentation:
Release Notes,
Compilation Guide,
and Garbage Collection Guide.
This Practical Introduction to Achieving Determinism document and the complementary
Practical Introduction to Achieving Determinism Annex together describe how to use Java RTS
to quickly and easily achieve determinism. The Annex explains
exactly how the programs work, the input to the programs,
and the output from the programs. This current document
describes the scenarios in the script, as well as, for each scenario,
the conclusions that can be drawn from the results of that run,
often compared to the results from other runs. Here is a summary
of those conclusions:
-
Running on a vanilla VM cannot be deterministic, because
of the time-sharing policy of the Java VM.
-
A JLT cannot be deterministic, because it can be preempted
by garbage collection at any moment, regardless of its priority.
-
JIT compilation occurs at unpredictable times, causing an
unknown amount of jitter at unknown times during execution.
-
Using ITC and precompiling critical methods improves
determinism. See the Compilation Guide.
-
Running a critical RTT on Java RTS can be truly
deterministic, even with a huge CPU load, additional threads, and lots
of
memory consumption
and garbage collection.
-
The RTGC works within the thread priorities to manage
garbage collection at optimal times.
-
Priorities must be set properly to take advantage of the
fact that critical threads are not preempted by other threads, even the
GC.
-
If a thread has hard real-time constraints, set its priority
to critical. A critical RTT thread is defined by having a priority
higher than the "critical priority" of the RTGC. This means that the
RTGC will not preempt these threads for garbage collection. To protect
critical threads from allocations by non-critical threads, memory must
be reserved using an important parameter: RTGCCriticalReservedBytes .
Only critical threads will be able
to continue memory allocation when the critical reserved bytes
threshold is reached, and still behave deterministically if RTGC can
recycle enough memory faster than the allocation rate of the critical
threads. See the Garbage Collection Guide.
-
While an application is running, and consuming memory
in a regular fashion, the RTGC can tune its
behavior advantageously by observing the memory allocation activity and history.
This self-tuning can help in achieving deterministic behavior
for soft real-time threads. Using the NormalMinFreeBytes
command-line parameter can speed up the self-tuning of the RTGC.
See the Java RTS Garbage
Collection Guide
for a detailed explanation.
You can also specify a small pause in the critical thread
to let the RTGC get some CPU time to do collection.
-
Adding processors can help an application that is already
deterministic.
Based on the above discussion, the overall conclusion is that
real-time determinism can be easily achieved with Java RTS by
performing the following steps:
-
For the threads that should behave more deterministically,
convert instances of java.lang.Thread to
instances of javax.realtime.RealtimeThread.
-
Use ITC compilation, specifying critical methods to
precompile.
-
Use the Real-Time Garbage Collector.
-
Correctly assign priorities, identifying the few critical
threads.
-
Tune the RTGCCriticalReservedBytes
threshold value for the critical threads.
See the Practical Introduction to Achieving Determinism Annex
for complementary, more detailed information about the elements
described in this document.
[Contents]
Glossary
GC - Garbage Collection, or Garbage Collector
ITC - Initialization-Time Compilation
Java RTS - Sun Java Real-Time System
JIT - Just-In-Time compilation
Jitter - A measure of the variation in a particular response time.
JLT - An instance of the java.lang.Thread class.
JSR - Java Specification Request
NHRT - No Heap Real-Time Thread. This is a thread that cannot be
interrupted by garbage collection.
RTGC - Real-Time Garbage Collection
RTSJ - Real-Time Specification for Java
RTT - An instance of the javax.realtime.RealtimeThread class.
[Contents]
Copyright © 2007 Sun Microsystems, Inc. All Rights Reserved.
|
|