|
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) or the Linux Operating System.
The Practical Introduction
to Achieving Determinism Annex
document contains complementary, more detailed information about the
elements described in this document.
Technical Documentation: Links to all the Java RTS technical documents
Contents
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. Worst-case response times must be
deterministic, in other words, predictable within given limits. 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 for a particular
computational scenario of interest within a given executing application.
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 on Solaris OS,
GettingStarted.tar.gz on Linux),
which contains the example programs that support the explanations and
comments. These are very simple programs that iterate a predefined sequence of
calls to perform calculation and memory allocation. 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 the CPU, the
load on memory allocation and garbage collection, the length of time
a scheduled thread pauses (relinguishes control) 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
Achieving better determinism in an application depends primarily 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.
Static Initialization
The Java VM normally initializes 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 initialized,
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 initialized, 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 initialized
before the application starts executing. In addition, you can request
Java RTS to generate this list 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 methods are marked for ITC
compilation. When a class is initialized, the methods in it that are marked are
compiled at that time. You specify these methods in a list that is passed as a
command line argument. 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
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 initialized while the
program is in its steady state. For the Deterministic and GCDeterministic
programs, we use the recommended way of specifying preinitialization: We build
a preinitialization list 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.
The bench threads and the stress threads accept a user-specified pause_time parameter
to define a length of time that the thread pauses in order to allow other threads
(for example, garbage collection and JIT compilation) to execute. However, this pause_time is
implemented differently for a bench thread and for a stress thread.
- For the bench thread, we use wait-for-next-period logic.
The program first calculates the average execution time
for a certain number of iterations of the logic, including garbage collection.
This is called the "stress cost." To this is added the value of the pause_time
parameter, which represents additional inactive time, in order to arrive at
the calculated period.
- For the stress thread, the pause_time is the time
the thread will sleep within each of the outer loops.
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.
[Contents]
Multiprocessing Notes
The scenarios that are provided in the "Getting Started" package were written to be
run on Solaris OS. However, the script can also run on Linux.
Several of the scenarios use Solaris 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.
The equivalent functionality on Linux includes processor
affinity and cpusets, in order to restrict the set of processors used by the tests.
In addition, the processors in the processor sets are specified to be
non-interruptible.
However, you can use the script as-is (on Solaris or Linux) to run the scenarios on a
single-processor machine, although some of the scenarios,
mainly those for the GCDeterministic program, require
a 4-CPU machine. If you have a single-CPU machine, just remove
all instances of the following command in the script file:
- Solaris:
psrset
- Linux:
taskset
Note that removing these commands in the GCDeterministic
scenarios will affect deterministic behavior.
For information about using processor sets on Solaris OS, see the section
on using multiprocessors in the
Java RTS Technical Information document, as well as the article
Solaris Processor Sets Made Easy.
[Contents]
Purpose of the Scenarios
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 occurs.
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 four of these scenarios
force the run on only one processor. The last two use two
processors. If you run the scenarios with the script file provided,
make sure that you run them on a multiprocessor machine (more
than two processors).
-
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.
[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, for all scenarios, this document describes
the summary section of the output results, which is the part of the output
that is used for interpreting the results. The time unit of the
recorded values is the microsecond, unless otherwise indicated.
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 U6.
Summary section of the output results:
=====================================
Summary of results:
=====================================
Mean execution time: 377 microseconds
Best execution time: 308 microseconds
Worst execution time: 160331 microseconds
Most frequent execution time: 313 microseconds, 345 occurrences
Execution time jitter: 160023
Standard deviation: 2886.58
=====================================
This summary shows, for the given run:
The mean execution time for all the 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.
[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 on 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.
Note that only the Summary section of the output is shown here, although the "Comments"
often refer to detailed output that is displayed with the verbose option.
-
Run1: One bench JLT running at max priority, one stress JLT running at
min priority, no GC activity, 5000 iterations measured.
Output result
excerpt:
==========================================
Summary of results:
==========================================
Mean execution time: 361 microseconds
Best execution time: 307 microseconds
Worst execution time: 120316 microseconds
Most frequent execution time: 312 microseconds, 1266 occurrences
Execution time jitter: 120009
Standard deviation: 2118.2
==========================================
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 normal priority, and the execution is longer (around
time-sharing switch time); 1000 iterations.
Output result excerpt:
==========================================
Summary of results:
==========================================
Mean execution time: 25116 microseconds
Best execution time: 12356 microseconds
Worst execution time: 562526 microseconds
Most frequent execution time: 12438 microseconds, 18 occurrences
Execution time jitter: 550170
Standard deviation: 39946.29
==========================================
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 128 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 0-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:
==========================================
Summary of results:
==========================================
Mean execution time: 15338 microseconds
Best execution time: 12350 microseconds
Worst execution time: 172418 microseconds
Most frequent execution time: 12504 microseconds, 28 occurrences
Execution time jitter: 160068
Standard deviation: 15119.80
==========================================
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 0-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: Only one bench JLT thread running at max priority,
execution time around time-sharing switch time, 1000 iterations.
Output result
excerpt:
==========================================
Summary of results:
==========================================
Mean execution time: 12506 microseconds
Best execution time: 12372 microseconds
Worst execution time: 13231 microseconds
Most frequent execution time: 12450 microseconds, 24 occurrences
Execution time jitter: 859
Standard deviation: 94.89
==========================================
Comments:
There are only 7 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:
==========================================
Summary of results:
==========================================
Mean execution time: 37717 microseconds
Best execution time: 12315 microseconds
Worst execution time: 432595 microseconds
Most frequent execution time: 12491 microseconds, 32 occurrences
Execution time jitter: 420280
Standard deviation: 67965.83
==========================================
Comments:
The number of outliers has dramatically
increased, the deviations are large, and most of the consecutive execution
times show differences of greater than 100 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:
==========================================
Summary of results:
==========================================
Mean execution time: 16587 microseconds
Best execution time: 12350 microseconds
Worst execution time: 412557 microseconds
Most frequent execution time: 12481 microseconds, 53 occurrences
Execution time jitter: 400207
Standard deviation: 24848.16
==========================================
Comments:
Deviations are still large, as shown by the 51 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 greater than 100 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. There are 5000 iterations.
Output result
excerpt:
==========================================
Summary of results:
==========================================
Mean execution time: 2929 microseconds
Best execution time: 2772 microseconds
Worst execution time: 4031 microseconds
Most frequent execution time: 2884 microseconds, 176 occurrences
Execution time jitter: 1259
Standard deviation: 101.30
==========================================
Comments:
Results are much better but still not sufficient. There are 24 outliers
due to GC activity stopping the bench thread. Deviations between
consecutive execution times are larger. Jitter is greater than 1 millisecond.
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 of 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.
Note that only the Summary section of the output is shown here, although the "Comments"
often refer to detailed output that is displayed with the verbose option.
-
Run1: Runs an RTT as the bench thread running at maximum
real-time priority and a JLT running at minimum priority (with
Fibonacci calculations only) as a stress thread. Runs on 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:
==========================================
Summary of results:
==========================================
Mean execution time: 687 microseconds
Best execution time: 687 microseconds
Worst execution time: 728 microseconds
Most frequent execution time: 687 microseconds, 668 occurrences
Execution time jitter: 41
Standard deviation: 5.12
===========================================
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 (687 microseconds). This reference value
is found 668 times out of 1000 iterations. The worst deviation between
two consecutive iterations is only 39 microseconds, and happens only once.
The jitter figure (41 microseconds) and standard deviation figure
(5 microseconds) are excellent. 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:
==========================================
Summary of results:
==========================================
Mean execution time: 689 microseconds
Best execution time: 687 microseconds
Worst execution time: 729 microseconds
Most frequent execution time: 690 microseconds, 631 occurrences
Execution time jitter: 42
Standard deviation: 5.27
==========================================
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 preinit lists.
Output result
excerpt:
==========================================
Summary of results:
==========================================
Mean execution time: 685 microseconds
Best execution time: 681 microseconds
Worst execution time: 722 microseconds
Most frequent execution time: 686 microseconds, 1389 occurrences
Execution time jitter: 41
Standard deviation: 4.94
==========================================
Comments:
Using the ITC compilation system improves determinism. There are 1389
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).
The provided script uses
processor sets on Solaris (or processor affinity on Linux) to
illustrate the distributed execution of the RTGC. You can also run the
program without processor sets or processor affinity, but this will change the behavior
of the scenarios depending on the number of available CPUs on your machine
and the load. See Multiprocessing Notes.
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.
Note that only the Summary section of the output is shown here, although the "Comments"
often refer to detailed output that is displayed with the verbose option.
-
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 processor.
There are 1000 iterations.
Output result
excerpt:
==========================================
Summary of results:
==========================================
Mean execution time: 173 microseconds
Best execution time: 172 microseconds
Worst execution time: 184 microseconds
Most frequent execution time: 173 microseconds, 409 occurrences
Execution time jitter: 12
Standard deviation: 1.50
==========================================
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 12 microseconds, and standard deviation is 1.5 microsecond.
These are excellent results (like Run3 of Deterministic scenarios).
Interpretation:
Again the real-time priority and scheduling model of
Java RTS came into play 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:
==========================================
Summary of results:
==========================================
Mean execution time: 985 microseconds
Best execution time: 639 microseconds
Worst execution time: 61277 microseconds
Most frequent execution time: 683 microseconds, 38 occurrences
Execution time jitter: 60638
Standard deviation: 3838.29
==========================================
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 10
microseconds). The bench thread is blocked 6 times out of 20 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 on one processor.
Output result
excerpt:
==========================================
Summary of results:
==========================================
Mean execution time: 1006 microseconds
Best execution time: 664 microseconds
Worst execution time: 49769 microseconds
Most frequent execution time: 683 microseconds, 53 occurrences
Execution time jitter: 49105
Standard deviation: 3711.62
==========================================
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:
==========================================
Summary of results:
==========================================
Mean execution time: 680 microseconds
Best execution time: 592 microseconds
Worst execution time: 1046 microseconds
Most frequent execution time: 680 microseconds, 60 occurrences
Execution time jitter: 454
Standard deviation: 19.93
==========================================
Comments:
There is only one deviation to previous that is more than 500 microseconds.
Moreover, the worst deviation is less than 500 microseconds from the reference value
(366 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 two CPUs.
There are 1000 iterations.
Output result
excerpt:
==========================================
Summary of results:
==========================================
Mean execution time: 792 microseconds
Best execution time: 610 microseconds
Worst execution time: 1086 microseconds
Most frequent execution time: 771 microseconds, 17 occurrences
Execution time jitter: 476
Standard deviation: 83.99
==========================================
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:
==========================================
Summary of results:
==========================================
Mean execution time: 264 microseconds
Best execution time: 220 microseconds
Worst execution time: 481 microseconds
Most frequent execution time: 275 microseconds, 37 occurrences
Execution time jitter: 261
Standard deviation: 21.55
==========================================
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 30 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 on a single CPU.
There are 1000 iterations.
Output result
excerpt:
==========================================
Summary of results:
==========================================
Mean execution time: 172 microseconds
Best execution time: 172 microseconds
Worst execution time: 206 microseconds
Most frequent execution time: 173 microseconds, 466 occurrences
Execution time jitter: 34
Standard deviation: 1.85
==========================================
Comments:
The results are very similar to those of Run1. Only one deviation is
33 microseconds, with a few deviations less than 10 microseconds. Standard
deviation is about 2 microseconds. 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:
==========================================
Summary of results:
==========================================
Mean execution time: 1352 microseconds
Best execution time: 629 microseconds
Worst execution time: 63080 microseconds
Most frequent execution time: 656 microseconds, 30 occurrences
Execution time jitter: 62451
Standard deviation: 5775.48
==========================================
Comments:
Determinism has been somewhat lost, but except for the loops
where the RTGC had to come into play on the only available CPU, the run
was very stable.
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 9 noticeable 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:
==========================================
Summary of results:
==========================================
Mean execution time: 1447 microseconds
Best execution time: 626 microseconds
Worst execution time: 47475 microseconds
Most frequent execution time: 652 microseconds, 39 occurrences
Execution time jitter: 46849
Standard deviation: 5461.10
==========================================
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 6 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:
==========================================
Summary of results:
==========================================
Mean execution time: 1498 microseconds
Best execution time: 871 microseconds
Worst execution time: 61073 microseconds
Most frequent execution time: 918 microseconds, 82 occurrences
Execution time jitter: 60202
Standard deviation: 5679.13
==========================================
Comments:
Trying to let RTGC do collection when the bench RTT is idle is a good idea.
The RTGC has been able to adapt to the allocation situation of the program.
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:
==========================================
Summary of results:
==========================================
Mean execution time: 872 microseconds
Best execution time: 507 microseconds
Worst execution time: 62287 microseconds
Most frequent execution time: 689 microseconds, 53 occurrences
Execution time jitter: 61780
Standard deviation: 3370.91
==========================================
Comments:
Auto-tuning of the RTGC happens now very rapidly, after 2 GC cycles. The RTGC
has been able to adapt to the situation and avoid any jitter on the
bench thread after the first 180 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 30 megabytes. There are 1000 iterations.
Output result
excerpt:
==========================================
Summary of results:
==========================================
Mean execution time: 674 microseconds
Best execution time: 573 microseconds
Worst execution time: 900 microseconds
Most frequent execution time: 673 microseconds, 58 occurrences
Execution time jitter: 327
Standard deviation: 25.76
==========================================
Comments:
We now get excellent results. The RTGC now starts more quickly (as soon
as the remaining free memory goes below 30 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 12 microseconds. There is no deviation more than
230 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 (GettingStarted.tar.gz on Linux).
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 (GettingStarted.tar.gz on Linux)
to your working directory.
-
Unzip the GettingStarted.zip file (GettingStarted.tar.gz on Linux).
$ 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.
Note: (Solaris OS) In order to execute the 64-bit Java RTS VM,
add the -d64 option to the command line, as described in
Executing the 64-Bit VM in
the Java RTS Installation Guide.
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>/bin/javac \
-classpath src:<Java-RTS-install-dir>/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>/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 on Solaris OS or processor affinity on Linux.
See Multiprocessing Notes.
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 (sleep time or additional inactive time in wait-for-next-period scheme)
- Multiprocessor environment
- Processor sets on Solaris OS or processor affinity on Linux
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, 2010, Oracle Corporation and/or its affiliates
|
|