2. our optimization

advertisement
Optimizing Java Programs using Generic Types
Eli Mayfield
Kyle Roth
Elena Machkasova Daniel Selifonov Nathan Dahlberg
mayf0016, roth0323, elenam, selif004, dahlb061@morris.umn.edu
University of Minnesota, Morris
ABSTRACT
Our research involves improving performance of programs written
in the Java programming language. By selective specialization of
generic types, we can enable the compiler to eliminate
typecasting, and provide type information to remove dynamic
method lookup at runtime. An example of this specialization
using Quicksort improved performance by over 20%.
Categories and Subject Descriptors
D.3.3 Language Constructs and Features - Classes and objects,
Inheritance, Polymorphism D.3.4 Processors - Optimization
General Terms
Performance, Languages
1. INTRODUCTION
Generic types were introduced into the Java programming
language in October 2004. Generic types ensure type safety,
guaranteeing that all data used will match a type parameter. Java
implements generic types by type erasure; after objects are
checked to match the type parameter, type information is
removed. Typecasts to specific instance types are then inserted by
the compiler.
Most Java compilers convert programs into platform-independent
bytecode which is then ran by the Java virtual machine, or JVM.
Just-In-Time compilers were added to the JVM to improve
performance through dynamic compilation, converting bytecode
into native code as a program runs. Sun Microsystems developed
the HotSpot JVM to selectively compile only frequently called
methods into native code, decreasing the overhead caused by JIT
compilers.
HotSpot has two modes, Client and Server. Client mode starts
programs as quickly as possible, while Server mode focuses on
optimizing the runtime of the program, performing more effective
but time-consuming optimizations. As a result, long programs
often (though not always) run faster in Server mode. Because our
optimization is most beneficial to large computations, our results
are listed in Server mode times. Client mode results differ from
Server mode, but our goal is to improve Server mode while
maintaining, and hopefully improving, Client mode performance.
2. OUR OPTIMIZATION
We optimize programs by selectively creating copies of generic
Permission to make digital or hard copies of all or part of this
work for personal or classroom use is granted without fee
provided that copies are not made or distributed for profit or
commercial advantage and that copies bear this notice and the
full citation on the first page. To copy otherwise, or republish, to
post on servers or to redistribute to lists, requires prior specific
permission and/or a fee.
classes with more specific class bounds. A general-purpose
container such as ArrayList would have a type parameter of <E>.
We can specialize ArrayList by creating a separate copy for each
type of data it will hold. For instance, if a program uses
ArrayLists to store its data of Integers and Strings, our
optimization would create specialized ArrayLists with bounds of
<E extends Integer> and <E extends String>.
This changes the names of the classes (for instance, from
ArrayList to ArrayListInteger and ArrayListString), so references
have to be changed in the rest of the program. This change can
also be made to classes with parameters to begin with; for
example, a Quicksort class might be parameterized to <T
extends Comparable<T>>, which can be specialized in the
same way.
Specializing classes to have more specific bounds improves
performance due to method lookup. With a general type, the Java
compiler must find the closest method in the inheritance tree; if
the type parameter of the class is specific enough, the Java
compiler can use devirtualization to call a method directly with no
runtime lookup at all.
There are several limitations to our optimization. This is a wholeprogram optimization – all code must be known. We assume
there is no dynamic code loading or reflection. Specializing the
bound is only effective when the new bound is specific enough
that it uniquely determines the method whose call we are trying to
optimize. We cannot specialize any class that uses static data; this
data is shared between instances of the class and creating
specialized copies of the class would create multiple copies of the
data. Additionally, classes with explicit type-erased operations
cannot be optimized since changing the bounds changes the type
used in the type erasure.
2.1 Partial Optimization
Not all classes in a program need to be specialized in order to see
results of specialization. A complete specialization would
specialize every parameterized class used in a program, and all the
parameterized classes they inherit from or extend, continuing all
the way up their inheritance tree. We can also specialize a
program without this much change by only specializing the
classes directly used in the program, or even only one or two
classes which will make the most difference Such optimizations
are referred to as partial specialization.
3. RESULTS USING QUICKSORT
As an example to find the effects of our optimization, we use an
implementation of Quicksort. This implementation took in an
ArrayList and sorted its data using the compareTo() method,
choosing a random pivot point at each step. By sorting large lists
of data many times, we were able to measure the improvement in
performance caused by our optimization: to this end, we used two
lists of 1,500 numbers, randomly generated with a constant seed;
one list stored the numbers as Integers and one list stored the
numbers as Strings. We used our own copies of the standard Java
code for these classes, along with the Collections classes. Each
test looped through this process 30 times, and we executed twenty
tests per run.
3.1 Variations in Optimizations
3.1.1 Original
We tested five variants of Quicksort.
The Original (O)
implementation of Quicksort used a Quicksort class parameterized
to <T extends Comparable<T>>, and stored data in
ArrayLists parameterized to <E>.
3.1.2 ArrayList
The ArrayList (AL) implementation used an un-optimized
Quicksort class, but stored data in specialized ArrayLists, an
ArrayListInteger parameterized to <E extends Integer>
and an ArrayListString parameterized to <E extends
String>.
3.1.3 Quicksort
The Quicksort (QS) implementation used specialized copies of
Quicksort, a QuicksortInteger parameterized to <T extends
Integer> and a QuicksortString parameterized to <T
extends String>, but used un-optimized ArrayLists.
3.1.4 QuicksortArrayList
The QuicksortArrayList (QSAL) implementation used the
specialized Quicksort classes from QS and the specialized
ArrayList classes from AL.
3.1.5 Complete
Finally, the Complete (C) implementation used the specialized
Quicksort and ArrayList classes, and also specialized all other
classes used by ArrayList that could be specialized (Iterable,
AbstractCollection, AbstractList, Collection, Iterator, and List).
11 seconds. This would suggest an improvement of about 27%
for this particular example.
4. CONCLUSIONS
The differences in performance can be attributed clearly to our
optimization. Specializing the Quicksort class allows
compareTo() to be called directly from Integer or String due to
devirtualization, removing the time spent from method lookup at
runtime. When the ArrayList class is specialized, the bytecode
implementation of the program changes from retrieving elements
changes from Objects:
ArrayList s = new ArrayList();
String in = “hello”;
s.insert(str);
String out = (String) s.get(0);
to a more succinct variant returning Strings, removing the need
for a typecast:
ArrayListStr s = new ArrayListStr();
String in = “hello”;
s.insert(str);
String out = s.get(0);
Specializing type parameters is clearly beneficial to the
performance of a program. It removes runtime method lookup,
and in programs where this is a significant portion of the time
spent overall (such as Quicksort), it makes a significant change.
The downside of this optimization is that it causes significant
code duplication – especially Complete specialization. However,
this drawback is not as pronounced with partial optimizations, and
some partial optimizations can perform just as well as (and
occasionally better than) a complete specialization. We saw
substantial speed improvements to modifying a single class in the
Quicksort implementation, which is very promising.
5. FUTURE WORK
Much can still be examined about how to determine which
programs will benefit from the optimization, and what changes
should be made in a partial optimization such that they cause the
greatest change in performance. The next goal is to develop an
algorithm which finds where optimizations can be performed and
performs the optimization automatically, whenever possible and
beneficial. We are also considering an interactive tool that allows
programmers to perform the specialization interactively when the
fully automatic transformation is not possible.
6. REFERENCES
Figure 1. Comparing Partial Quicksort Optimizations.
3.2 Results
.
The times we measured included time taken to insert the 3,000
elements into the two lists, then sort them using compareTo() with
our Quicksort class. The Original implementation took about 15
seconds to do this for each run. The ArrayList implementation
took about 12 seconds; all of our other optimizations took about
[1] Daniel Selifonov, Nathan Dahlberg, Elena Machkasova. On
the Algorithm for Specializing Java Programs with Generic
Types. Midwest Instruction and Computing Symposium
2007
[2] Gilad Bracha. Generics in the Java Programming
Language. Java Tutorials, etc., etc., look up specifics later.
[3] The Java Language Specification. More bibliographical stuff
here
Download