A Practical Introduction to Achieving Determinism
Sun Java Real-Time System 2.0_01
  

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.

  1. 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.

  2. 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.

  3. 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.

  4. 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?

  5. 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.

  6. 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.

  7. 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.

  1. 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.

  2. 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.

  3. 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.

  1. 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.

  2. 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.

  3. 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.

  4. 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.

  5. 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.

  6. 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.

  7. 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.

  8. 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.

  9. 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.

  10. 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.

  11. 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.

  12. 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:

  1. Copy the GettingStarted.zip file to your working directory.

  2. Unzip the GettingStarted.zip file.

    $ unzip GettingStarted.zip
    $ ls
    GettingStarted.jar run-scenarios.sh src
  3. 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:

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:

  1. For the threads that should behave more deterministically, convert instances of java.lang.Thread to instances of javax.realtime.RealtimeThread.

  2. Use ITC compilation, specifying critical methods to precompile.

  3. Use the Real-Time Garbage Collector.

  4. Correctly assign priorities, identifying the few critical threads.

  5. 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.