Chapter 5. Experimental Results
5.2 Benchmark Analysis
5.2.1 Scenario One Benchmark Analysis
The first benchmark focuses on observing the time overheads of exception handling process that searches the exception routines containing in a same method.
Each program in the scenario one has different numbers of catch statements. A test program in scenario one benchmark is shown in Fig 21. The program contains five catch statements resident to a try block that throws an ArithmeticException object by bytecode “athrow”. The Exception_x classes in the first to fifth catch statements are child classes of Throwable class. In other words, the thrown exception object cannot be handled by the first four exception routines. However the thrown object can be caught in the fifth exception routine in line 13 because the routine is defined to catch the
ArithmeticException object. The Java program then jumps to the exception routine five in line 14 to execute the exception routine. To observe the time overheads due to numbers of searched exception routine, each test program in scenario one benchmark has different numbers of searched exception routines.
Fig 21. A test program with exception thrown by bytecode “athrow”.
Fig 22. A test program with exception thrown by JVM.
In addition to the exception events thrown by bytecode “athrow”, the exceptions thrown by JVM are also tested in the scenario one benchmark. Another test program
1: public class Scenario_one_thrown_by _athrow{
2: public static void main(String args[]){
3: try{
2: public static void main(String args[]){
3: try{
with the exception thrown by JVM is shown in Fig 22. The test program triggers the ArithmeticException type exception implicitly in line 4 due to the wrong arithmetic operation in Java runtime system. Same as the test programs shown in Fig 21, the exception event is also caught by the fifth catch statement in line 13.
The results of scenario one benchmark are shown in Table II. The column “# of searched routines” shows the number of routine comparisons before the suitable exception handling routine is located. It is obvious from Table II that the exception handling overheads of the Java core is much smaller than that of a software-based Java VM. More important, the Sun CVM with the Just-In-Time compilation has even worse performance than the Sun CVM without JIT which is a common technique to accelerate execution of regular Java programs. In the table II, it shows that the JIT has additional overheads when a Java program encounters the exception handling process.
As shown in the row one, the overheads of CVM_JIT are 356 times larger than the overheads of dual-core Java and the overheads of CVM are 183 times larger than the overheads of dual-core Java.
Table II. Time overheads of exception handling process in Scenario One programs
# searched
Table III. Time overheads of adding an exception routine in the same try block.
The averaged overheads of exception handling process when adding an additional searched exception routine are shown in Table III. The table shows that it costs 87 cycles on additional exception routine comparing process in Sun CVM. To the dual-core Java, such overheads only cost 17 cycles. It is smaller than the overheads than the software JVM because the comparing process is mainly performed by sequentially comparing the registers with items in a compact on-chip table, which is efficient in hardware solution. And such the process is performed entirely in hardware Java core without external memory or involving interrupt service routines. In the row two in table II, the test program involves five times of searching process. The searching process cost 62 cycles which occupy 84% of the exception handling process.
To the CVM, the searching process cost 5*87 = 435 cycles which occupy 19% of the exception handling process. In tells that the software CVM has extra overheads to be ready to search for a suitable exception routine. To the hardware Java core, the process of searching for an exception event in lookup table is performed immediately after it extracts the thrown exception information which only costs 3 cycles. Therefore the exception event can be fast handled by the exception handling unit when the Java program encounters an exception.
In the Table II, we can see that the programs of case two have larger overheads than the programs with same numbers of searched routines in the case 1. The reason is simple that for the handling process for the exception event thrown by JVM, it has an extra step to instantiate the exception object. To Java core, such the process of instantiation of an object is performed by the interrupt service routine which has large
Dual-Core Java Sun CVM Sun CVM_JIT Averaged overheads of exception
handling process when adding a searched exception routine (cycles)
17 87 89
overheads due to inter processors communication. However the CVM and the CVM-JIT also has such larger overheads on object instantiation. In the programs of case two, the overheads of exception handling process in CVM are at most 4 times larger than the overheads in the dual-core Java. And the overheads of exception handling process in CVM-JIT are at most 5 times larger than the overheads in the dual-core Java. In the next section, a more complicate scenario will be discussed.
5.2.2 Scenario Two Benchmark Analysis
Fig 23. A test program containing five methods and the last method triggers an exception.
In the benchmark of scenario two, the suitable exception handling routine is designed to be located in one of the caller methods. A test program that has five called methods is shown in Fig 23. In the test program, an arithmetic exception is thrown in the last called method. And to find the suitable exception routine to catch this exception, the exception is handed over to each caller method. Finally in the main
1: public class Scenario_two_thrown_by _athrow{
2: public static void main(String args[]){
3: try{
4: method1();
5 : }catch (ArithmeticException e ){
6: //exception routine 0 7: }
8: } 9:
10: public static void method1(){
11: method2();
12: }
13: public static void method2(){
14: method3();
15: }
16: public static void method3(){
17: method4();
18: }
19: public static void method4(){
20: throw new ArithmeticException ();
21: } 22: }
method, an exception routine defined in line 5 can catch the ArithmeticException type exception event. Next, the program jumps to the line 6 and executes exception routine 0. Other test programs in the scenario two have different numbers of called methods.
The time overheads of exception handling process in each test program are shown in Table IV. In the Table IV, the column “# stack unwinding” stands for the number of stack that be unwind to find the suitable routine. A count of ‘0’ means that the search stops at the current main method where the exception is thrown. Again, the proposed dual-core Java outperforms the CVM and the CVM_JIT. And the CVM_JIT also has the worst performance. Comparing to the scenario one, the programs in scenario two have larger overheads on exception handling process. The reason is simple because that the exception handling processes in scenario two involve the processes of stack unwinding. In the row one in table IV, it shows that the overhead of exception handling process in the dual-core Java is only 11 cycles, which is 356 times smaller than the overheads in Sun JVM_JIT. Note the program tested in row one only contain a main method so that the thrown exception is caught in the first try block, which does not involve the process of stack unwinding.
Table IV. Overheads of exception handling process in the benchmark of scenario two.
Table V. Overheads when adding an additional searched method.
Thrown Exception
Dual-Core Java Sun CVM Sun CVM_JIT Overheads of exception handling process when
adding a method without a try block (cycles) 36 377 445
In other test programs, the overheads start to grow larger due to the processes of stack unwinding. In the row two of the Table IV, the overheads of exception handling process in the dual-core Java grows to 137 cycles. Comparing to the row one, the difference of overheads 137 – 11 = 126 cycles are the overheads that incurs 4 times of stack unwinding process. The average overheads when adding a searched method are calculated and shown in Table V. In the Table V, we can see that it costs average 36 cycles for exception handling unit to perform the stack unwinding process in Java core.
In such process, about 80% overheads are for an external memory access which average costs 27 cycles. As mentioned in chapter 5, the external memory access is to load the local variables count of a method, which is used to locate the return stack frame in the stack unwinding process. If the data of local variables count of a method can recorded in the on-chip table, the overheads for stack unwinding can be reduced to only 7 cycles. However concerning the precious usage on-chip memory space, we leave the data of local variables count to be saved in DDR-SDRAM.
5.2.3 Scenario Three Benchmark Analysis
Similar to the programs in scenario two, the test programs in scenario three also triggers exception event in the last called method. The difference is that the each called methods contains a try block with an exception routine. A test program that has five called methods is shown in Fig 24. In the test program, an arithmetic exception is thrown within a try block in method4. And the resident exception routine cannot handle the exception because the ArithmeticException class is not the sub class of Exception_4. Then the exception is handed over to the caller method to find a suitable exception routine. Again in method3, the exception cannot be handled and is handed over to the call method. The actions of handing over the exception continue until the exception is handed over to the main method. The main contains an exception routine
which can catch the ArithmeticException type exception. In the end, the program jumps to the line 6 and executes exception routine 0. Other test programs in scenario three have different numbers of called methods.
Fig 24. A test program containing five methods and each method contains an exception routine.
The time overheads of exception handling process in each test program are shown in Table VI. In the Table VI, the column “# stack unwinding” stands for the number of stack that be unwind to find the suitable routine. A count of ‘0’ means that the search stops at the current main method where the exception is thrown. Again, the proposed dual-core Java outperforms the CVM and the CVM_JIT. And the CVM_JIT has the worst performance. Comparing to the scenario two, the programs in scenario three have larger overheads on exception handling process. The reason is simple that the exception handling processes in scenario three has additional processes of check exception routine in each called methods.
1: public class Scenario_three_thrown_by _athrow{
2: public static void main(String args[]){
3: try{
10: public static void method1(){
11: try{ method2(); }
12: catch (Exception_1 e ){ //exception routine 1 } 13: }
14: public static void method2(){
15: try{ method3(); }
16: catch (Exception_2 e ){ //exception routine 2 } 17: }
18: public static void method3(){
19: try{ method4(); }
20: catch (Exception_3 e ){ //exception routine 3 } 21: }
22: public static void method4(){
23: try{ throw new ArithmeticException (); } 24: catch (Exception_4 e ){ //exception routine 4 } 25: }
26: }
Table VI. Time overheads of exception handling process in the benchmark of scenario two.
Table VII. Overheads of additional try block in each method.
The average overheads when adding a called method are shown in Table VII. The results of scenario two remain in the row one and the results of scenario three are shown at row two. Comparing to the programs in the scenario two, we can see that the programs in the scenario three costs additional 19 cycles in dual-core Java. The 16 cycles are used to check an exception routine whether can catch the exception. Such the process involves class ID comparing or JPC ranges checking, which are efficient in hardware solution. For Sun CVM-JIT, we can see that such process has large additional costs if a searched method contain an additional exception routine, which adds ((837-445)/445= 88% overheads.
Considering the fact that the software-based JVM may runs on a general-purpose RISC processor at a frequency higher than that of the Java processor, we provide another experimental result that also tests the benchmark of scenario three. We compare the proposed dual-core Java with Sun CVM that runs on a 300 MHz PowerPC processor, which is the maximal processor clock rate for the Virtex-4 FPGA. In the dual-core Java platform, the Java processor remains at 83.3 MHz. The experimental results are shown in Table VIII. Note that the programs are executed 10000 times and
Thrown Exception
Dual-Core Java Sun CVM Sun CVM_JIT Overheads of exception handling process when
adding a method without a try block (cycles) 36 377 445
Overheads of exception handling process when
adding a method containing a try block (cycles) 55 460 837
the results are in milliseconds (ms). In table VIII, we can see that the dual-core Java processor still outperforms Sun CVM. By comparing the results in Table VI and Table VIII, we can see the performance of Sun CVM improved by the higher clock rate. In row one of table VIII, one can see that the exception handling overheads of the dual-core Java processor is 148 times smaller than that of the CVM-JIT, while the overhead ratio in Table VI is 356 times. Therefore, although CVM runs at 300/83.3 = 3.6 times higher clock rate, the performance only increases by 356/148 = 2.4 times. In any cases, the dual-core Java processor is a clear winner in exception handling.
Table VIII. The Sun CVM runs on a 300 MHz PowerPC platform and the Java processor in dual-core Java remains 83.3 MHz.
Thrown Exception Type # stack unwinding
Dual-Core Java Exception handling time
(ms)
Sun CVM Exception handling time
(ms)
Sun CVM_JIT Exception handling time
(ms)
Thrown by "athrow"
0 13 903 1928
4 236 1706 3412
9 522 2438 4931
14 810 3167 6696
Chapter 6. Conclusions and Future Works
In this thesis, we have proposed the design of the architecture to support the exception handling mechanism for a dual-core Java processor. Two of the key architecture proposals are the two-level runtime image hierarchy and the method area manager. The first-level method area offers the Java core a memory-efficiently way to execute a specific method. And to buffer the specific method routine recourses, the method area manager is designed to cooperate with two lookup tables. The two on-chip lookup tables in the method area manager also offer the Java core the capability to retrieve the class-based information and method-based information. For the dynamic method loading, the tables are used to locate the runtimes images in second-level memory space. For the exception handling process, the tables provide the class inheritance information and method exception routines information.
With the two tables and an additional exception lookup table, the proposed architecture can perform exception handling process mainly in Java core. In addition, we adopt the stack unwinding approach in exception handling processes which is suitable for hardware solution. By the stack unwinding approach, the exception overheads can be delayed to the time that the Java program actually throws an exception event. In the final experimental results, it shows that the proposed dual-core Java processor only cost 320 LUTs on the exception handling unit. And it also shows that the dual-core Java processor outperforms the software Sun CVM in exception handling process. Moreover, the Sun CVM with the support of JIT compilation has even worse performance on exception handling than the CVM without the JIT. Not to mention the additional memory overheads on JIT compilation. The dual-core Java may be better suitable for deeply-embedded smart devices.
In the future, the dual-core Java processor will supply more functional features for the Java language. For now, it is not capable of multi-threading executions. To take advantages of the hardware Java core, the hardware-based context-switching for multi-threading is considered to be performed well in Java core. However, the design may increase the memory space overheads such as the space of Java stack, which is another important issue in embedded application processer designs. Therefore a context-switching design without significantly increase the logic size would be investigated in the future.
Appendix: CLDC Exception Library
Class name Description
Exception
The class Exception and its subclasses are a form of Throwable that indicates conditions that a reasonable application might want to catch.
ClassNotFoundException
Thrown when an application tries to load in a class through its string name using: The forName method in class Class.
The findSystemClass method in class ClassLoader . The loadClass method in class ClassLoader. But no definition for the class with the specified name could be found.
IllegalAccessException
An IllegalAccessException is thrown when an application tries to reflectively create an instance (other than an array), set or get a field, or invoke a method, but the currently executing method does not have access to the definition of the specified class, field, method or constructor.
InstantiationException
Thrown when an application tries to create an instance of a class using the newInstance method in class Class, but the specified class object cannot be instantiated because it is an interface or is an abstract class.
InterruptedException
Thrown when a thread is waiting, sleeping, or otherwise paused for a long time and another thread interrupts it using the interrupt method in class Thread.
NullPointerException
Thrown when an application attempts to use null in a case where an object is required. These include: Calling the instance method of a null object. Accessing or modifying the field of a null object. Taking the length of null as if it were an array. Accessing or modifying the slots of null as if it were an array. Throwing null as if it were a Throwable value. Applications should throw instances of this class to indicate other illegal uses of the null object.
RuntimeException
RuntimeException is the superclass of those exceptions that can be thrown during the normal operation of the Java Virtual Machine. A method is not required to declare in its throws clause any subclasses of RuntimeException that might be thrown during the execution of the method but not caught.
ArithmeticException
Thrown when an exceptional arithmetic condition has occurred. For example, an integer "divide by zero" throws an instance of this class.
IndexOutOfBoundsException
Thrown to indicate that an index of some sort (such as to an array, to a string, or to a vector) is out of range.
Applications can subclass this class to indicate similar exceptions.
ArrayIndexOutOfBounds Exception
Extends IndexOutOfBoundsException. Thrown to indicate that an array has been accessed with an illegal index. The index is either negative or greater than or equal to the size of the array.
StringIndexOutOfBounds Exception
Extends IndexOutOfBoundsException. Thrown by String methods to indicate that an index is either negative or greater than the size of the string. For some methods such as the charAt method, this exception also is thrown when the index is equal to the size of the string.
Extends IndexOutOfBoundsException. Thrown by String methods to indicate that an index is either negative or greater than the size of the string. For some methods such as the charAt method, this exception also is thrown when the index is equal to the size of the string.