340 likes | 810 Views
Java for High Performance Computing Introduction to Java for HPC http://www.hpjava.org/courses/arl Instructor: Bryan Carpenter Pervasive Technology Labs Indiana University Java History
E N D
Java for High Performance Computing Introduction to Java for HPC http://www.hpjava.org/courses/arl Instructor: Bryan Carpenter Pervasive Technology Labs Indiana University dbcarpen@indiana.edu
Java History • The Java language grabbed public attention in 1995, with the release of the HotJava experimental Web browser, and the subsequent incorporation of Java into the Netscape browser. • Java had originally been developed—under the name of Oak—as an operating environment for PDAs, a few years before. • Very suddenly, Java became one of the most important programming languages in the industry. • The trend continued. Although Web applets proved less important than they originally seemed, Java was rapidly adopted by many other sectors of the programming community. • Within a year or so of its release, some people were suggesting that Java might be good for high performance scientific computing. • A workshop on Java for Science and Engineering Computation was held at Syracuse University in late 1996—a precursor of the subsequent Java Grande activities. dbcarpen@indiana.edu
Why Java for HPC? • Many people believed that in general Java provided a better programming platform than its precursors, and in particular that it was better adapted to the Internet. • In the parallel computing world there has been a long history of novel language concepts to support parallelism, multithreading etc. • Java seemed to incorporate at least some of these ideas, and it had the benefit that it was clearly going to be a mainstream language. • Essentially this analysis stands. The most likely reason for doing scientific computing in Java is that in general it encourages better software engineering than, say, Fortran, and there is a huge amount of excellent supporting software for Java. • But in 1996 there was a obvious blot on this landscape: performance. dbcarpen@indiana.edu
Preamble: High Performance? dbcarpen@indiana.edu
The Java Virtual Machine • Java programs are not compiled to machine code in the same way as conventional programming language. • To support safe execution of compiled code on multiple platforms (portability, security), they are compiled to instructions for an abstract machine called the Java Virtual Machine (JVM). • The JVM is a specification originally published by Sun Microsystems. • JVM instructions are called Java byte codes. They are stored in a class file. • The JVM is a program that runs on a “real” computer. So any compiled Java program (class file) can be run on any computer that has a JVM available. • This execution model is part of the specification of the Java platform. There are a few compilers from the Java language to machine code, but it is hard to get these recognized as “Java compliant”. • The first implementations of the JVM simply interpreted the byte codes. These implementations were very slow. • This led to a commonmisconception that Java is an interpreted language and inherently slow. dbcarpen@indiana.edu
Run-time Compilation • Modern JVMs normally perform some form of compilation from byte codes to machine code on the fly, as the Java program is executed. • In one form of Just-In-Time compilation, methods may be compiled to machine code immediately before they are executed for the first time. Then subsequent calls to the method just involve jumping into the machine code. • More sophisticated forms of adaptive compilation (like in the Sun Hotspot JVMs) initially run methods in interpreted mode, monitor program behavior, and only spend time compiling portions of the byte code where the program spends significant time. This allows more intelligent allocation of CPU time to compilation and optimization. • Modern JVMs (like the Hotspot server JVM) implement many of the most important kinds of optimization in used by static compilers for “traditional” programming languages. • Adaptive compilation may also allow some optimization approaches that are impractical for static compilers, because they don’t have the run-time information. dbcarpen@indiana.edu
Java Grande Forum • An early stimulus for adoption of Java technologies in large scale computation—especially scientific and technical—was the Java Grande Forum. • The Java Grande Forum (www.javagrande.org) met and held annual ACM conferences between 1997 and 2002. • Related workshops held in Melbourne and Nice this year (2003). • They proposed various improvements to Java for numerical and large scale computing, some of which were adopted into Java platform. • The Forum was divided into a Concurrency and Applications Working Group and a Numerics Working Group (http://math.nist.gov/javanumerics). • Later also spawned a Message Passing Working Group (https://mailer.csit.fsu.edu/mailman/listinfo/java-mpi) and a Benchmarking Activity (www.epcc.ed.ac.uk/javagrande). dbcarpen@indiana.edu
Numerics Working Group • Activities of the JG Numerics Working Group have embraced various issues related to numeric computation in Java: • Basic floating point semantics of the Java language—had an early success when their proposed strictfp modifier was adopted into the Java 2 language. • Libraries to support multidimensional arrays, complex arithmetic, linear algebra, higher transcendental functions. • Discussed language extensions, notably for multiarrays and complex numbers (also operator overloading, etc). • Produced two JSRs (Java Specification Requests) to the Java Community Process • One of them (Numeric Extensions) was withdrawn last year. The other (Multiarray Package) is still on the table. • Led by Ron Boisvert and Roldan Pozo of NIST. http://math.nist.gov/javanumerics dbcarpen@indiana.edu
Message Passing Working Group • The JG Message Passing Working Group met between 1998 and 1999, largely to address the gap left by the absence of any “official” binding for Java from the MPI forum. • The working group had representation from various teams responsible for early MPI-like systems for Java. • Led by Vladimir Getov. • A specification was published in: MPJ: MPI-like message passing for Java B. Carpenter, V. Getov, G. Judd, A. Skjellum, G. Fox Concurrency Practice and Experience, 12(11), 2000. • mpiJava was nominated as a “reference implementation”by the Java Grande Working group. dbcarpen@indiana.edu
Benchmarking Activity • Edinburgh University led a Benchmarking Activity in JG, and assembled a suite of (mainly) scientific benchmarks for Java: www.epcc.ed.ac.uk/javagrande • The suite is divided into 4 parts: • Sequential benchmarks, suitable for single processor execution. • Subdivided into low-level operations, kernels (7 basic algorithms), and 5 “large scale applications”. • multi-threaded benchmarks, suitable for parallel execution on shared memory multiprocessors. • Low-level thread benchmarks, and a parallel subset of the kernels and applications above. • MPJ benchmarks, suitable for parallel execution on distributed memory multiprocessors. • Low-level benchmarks for MPI-like primitives, and a parallelized subset of the kernels and applications above (developed using mpiJava). • Language comparison benchmarks, which are a subset of the sequential benchmarks translated into C. • Various results presented at Java Grande/ISCOPE 2001 conference… dbcarpen@indiana.edu
Benchmarking Java against C and Fortran • In a widely cited paper: “Benchmarking Java against C and Fortran for Scientific Applications” ACM Java Grande/ISCOPE 2001 Conference J. M. Bull, L. A. Smith, L. Pottage and R. Freeman presented evidence that Java performance was becoming competitive with C and Fortran. • They compared a subset of the kernel and application benchmarks from the JG benchmark suite against C and Fortran versions, using a variety of JVM implementations and native compilers, on four platforms: • Intel Pentium, Windows (NT) • Intel Pentium, Linux • Sun Ultrasparc • Compaq Alpha dbcarpen@indiana.edu
Tested Platforms and Compilers dbcarpen@indiana.edu
Example Timings (Kernels) dbcarpen@indiana.edu
Example Timings (Applications) dbcarpen@indiana.edu
All Results dbcarpen@indiana.edu
Mean Execution Time Ratios dbcarpen@indiana.edu
Remarks • Comparisons with C and C++ are generally very satisfactory, especially on the important Pentium platforms. • On Linux the IBM JDK 1.3 generally does best (except on MolDyn for some reason). • We usually use this JDK for our own benchmarks. • Java also compares favorably with g77 (GNU Fortran). • Comparisons with Portland Group Fortran on Linux are more troublesome. • pg77 is more than twice as fast as the best Java on MolDyn. • Comparison is more favorable on LUFact, but this code has been criticized by other authors. • Unfortunately there aren’t more Fortran results. • Of course the Portland compiler is highly tuned for scientific algorithms. dbcarpen@indiana.edu
NAS Parallel Benchmarks • For balance, see the less rosy view of Java performance reported in “Implementation of the NAS Parallel Benchmarks in Java”, Michael A. Frumkin, Matthew Schultz, Haoqiang Jin, and Jerry Yan, NAS Technical Report NAS-02-009. • Presented at IPDPS 2003, Nice, France • The translate the NAS benchmarks to Java, and study performance on various platforms: IBM p690, SGI Origin2000, Sun Enterprise10000, Linux PC, Apple Xserve. • Where they give Fortran results (only reported for IBM and SGI platforms), differences in performance are as much as an order of magnitude. • Unfortunately they emphasize SGI (not generally considered a good platform for Java), and don’t give Fortran results for the “commodity platforms”. • Their results for the IBM platform are intermediate—performance ratios of 3 or 4 typical (less for some benchmarks)? dbcarpen@indiana.edu
What I Think is the Situation • On Linux platforms (which of course are widely used for clusters), if you use the “free” compilers for C and Fortran (like gcc, g77), and if you use a good JVM like the one in the free IBM JDK, performance of Java, C, and Fortran are quite similar. • On the same platforms, if you use a proprietary Fortran compiler like the one from Portland, you will probably still see a significant speed advantage for Fortran. • The Edinburgh results suggest factors 2-3, but with extremely limited statistics! • On IBM platforms (where they have good Fortran compilers) a factor around 4 is probably still common. • And apparently there are still cases, like on the SGI, where an order of magnitude is common. • This is just my reading of the situation based on the few published benchmarks, plus our own experiences. It would be good to have a lot more data on this! dbcarpen@indiana.edu
Features of the Java Language dbcarpen@indiana.edu
Prerequisites • We assume you know either Java or C++ moderately well. • But some things, like threaded programming with Java and RMI, will be covered from an introductory level. • In this section I will only point out some features and terminologies are characteristic of Java and that you probably should understand. • And highlight some of the differences from C++. dbcarpen@indiana.edu
What Java Isn’t • Perhaps the main thing it isn’t is C++—after programming Java it for a long time, it’s hard to think of it as being very closely related to C++. • It has a similar syntax for expressions, control constructs, etc, but those are perhaps the least characteristic features of C++. • The mindset when you are programming Java is very different: • In C++ you use characteristic features like operator overloading, copy constructors, templates, etc, to create your own “little language”, as you write class libraries. • You often spend a lot of time worrying about memory management and efficient creation of objects. • Worry about inline versus virtual methods, pointers and references, minimizing overheads. • In Java most of these things features go away. • Minimal control over memory management, due to automatic garbage collection. • Highly dynamic language: all code is loaded dynamically on demand; implicit run-time descriptors play an important role, through run-time type checks, instanceof, etc. • Logically all methods are virtual; overloading and implementation of interfaces is ubiquitous. • Exceptions, rarely used in C++, are used universally in Java. dbcarpen@indiana.edu
Class Structure • In Java, all methods and non-local variables are explicitly member of classes (or interfaces). • No default, global, namespace (except the names of classes and interfaces). • Java discards multiple inheritance at the class level. Inheritance relations between classes are strictly tree-like. • Every class inheritance diagram has the universal base class Object at its root. • It introduces the important idea of an interface, which is logically different from a class. Interfaces contain no implementation code for the methods they define. • Multiple inheritance of interfaces is allowed, and this is how Java manages without it at the class level. • Since Java 1.2, classes and interfaces can be nested. This is a big change. dbcarpen@indiana.edu
Classes and Instances • Will consistently use the following terminologies (which are “correct”): • A class is a type, e.g. public class A {int x ; void foo() {}} • An interface is a type. • An instance is an object. An object is always an instance of one particular class. • That class may extend other classes and implement various interfaces. • Any expression in Java that has class type (or interface type) is a reference to some instance (or it is a null reference). E.g. a variable declared: A a ; holds a reference to an instance. The objects themselves are “behind the scenes” in Java: we can only manipulate pointers to them. • Also references to objects and arrays are the only kinds of pointer in Java. E.g. there are no pointers to fields or array elements or local variables. dbcarpen@indiana.edu
Instance and static members • The following terminologies are common. In: public class A { int x void foo() {…} static int y ; static void goo() {…} } We say: x is an or instance variable, or non-static field. foo() is an instance method, or non-static method. y is a static field, or class variable. goo() is a static method, or class method. dbcarpen@indiana.edu
Class Loading • A Java program is typically written as a class with a public, static, void, main() method, as follows public class MyProgram { public static void main(String [] args) { … body of program … } } and started by a command like: $ java MyProgram • What this command does is to create a Java Virtual Machine, load the class MyProgram into that JVM, then invoke the main() method of the class (which must have exactly the signature shown above). • It finds the class file for MyProgram (usually called MyProgram.class) by searching a list of directories, typically defined in the environment variable CLASSPATH. • As this process unfolds, dependencies on other class and interfaces and their supertypes will be encountered, e.g. through statements that use other classes. The class loader brings in the class files for these types on demand. Code is loaded, and methods linked, incrementally throughout execution. dbcarpen@indiana.edu
The CLASSPATH • Many people have problems getting the CLASSPATH environment variable right. • Because all linking is done at run-time, must ensure that this environment variable has the right class files on it. • There is a useful property called binary-compatibility between classes. This means that (within some specified limits) two class files that implement the same public interface can be used interchangeably. It also means that if you pick up an inappropriate implementation of a given class from the CLASSPATH at runtime, things can go wrong in an opaque way. • The class path is a colon-separated (semicolon-separated in Windows) list of directories and jar files. • If the class path is empty, it is equivalent to “.”. But if the class path is not empty, “.” is not included by default. • A directory entry means a root directory in which class files or package directories are stored; a jar entry means a jar archive in which class files or package directories are stored. dbcarpen@indiana.edu
Java Native Interface • Some methods in a class may be declared as native methods, e.g.: class B { public native long add(int [] nums) ; } Notice the method add() has the modifier native, and the body of the method declaration is missing • It is replaced by a semicolon, similar to abstract methods in interfaces, etc. But in this case the method isn’t abstract. • The implementation of a native method will be given in another language, typically C or C++ (we consider C). • Implementing native methods is quite involved. • It isn’t clear that is such a bad thing—maybe it discourages casual use! • In general there should be a very good reason for resorting to JNI. dbcarpen@indiana.edu
Implementing Native Methods • The required naming conventions and parameter lists of the native functions are fairly complicated. First compile the Java class with the native methods, then run the command javah to automatically generate C headers. • A generated file B.h contains a prototype like JNIEXPORT jlong JNICALL Java_B_add (JNIEnv *, jobject, jintArray); Here JNIEXPORT and JNICALL are macros whose expansion you don’t need to worry about (defined in standard headers). • Next you create B.c, in which you #include “B.h”, and give your definition for the function Java_B_add() . dbcarpen@indiana.edu
A Definition of Java_B_add() JNIEXPORT jlong JNICALL Java_B_add(JNIEnv * env, jobject this, jintArray nums) { jint *cnums ; int i, n ; jlong sum = 0 ; n = (*env)->GetArrayLen(env, nums) ; cnums = (*env)->GetIntArrayElements(env, nums, NULL) ; for(i = 0 ; i < n ; i++) sum += cnums [i] ; return sum ; } dbcarpen@indiana.edu
Remarks • Several C types are defined in standard jni.h headers that are handles to Java objects. • JNIEnvrepresents (somehow) the Java environment in which the method was invoked. This includes handles on the C data structures associated with the implementation of the JVM itself, and thread context. • jobject is a handle on a Java object (or the structure in the JVM that represents an object). In particular the parameter jobject this refers to the instance on which the method was called. • jintArray naturally represents a Java int[] array. • The functions GetArrayLenand GetIntArrayElements (actually function-valued fields of the JNIEnv struct) are effectively accessor methods for the jintArray handle. • jint, jlong are typedefs that most likely equate to just C int and long (depending on native word lengths). • In a real native library you must be much more careful about trapping error conditions and exceptions, otherwise you compromise the safety of Java. dbcarpen@indiana.edu
Linking • The file B.c is compiled, then linked into a shared object library (or dynamic linked library in Windows). • This library must be in a directory where the JVM can find it at run time (e.g. on the LD_LIBRARY_PATH). • If the library is called libB.so, the Java program must execute System.loadLibrary(“B”) ; to load the native library. • Finally Java program can call the add() method. dbcarpen@indiana.edu
The Invocation API • JNI also provides a very powerful mechanism for going the other way—calling from a C program into Java. • First the C program needs to create a JVM (initialize all the data structures associated with a running JVM), which it does with a suitable library call. • It ends up with a JNIEnv handle, which it can then use to call all the JNI C functions for operating on Java objects, just like those used from in a native method implementation. • Like GetIntArrayElements, but actually there are similar functions to do completely general things, like creating objects, calling methods on them, and so on. • The standard java command works exactly this way—it uses the JNI invocation API to create a JVM, and call the main() method of the class specified on the command line. dbcarpen@indiana.edu
The Rest of this Course • Will cover three core topics: • Multithreaded and Shared-Memory Programming in Java • Java as a multithreaded language; Java thread synchronization primitives. • Java threads for Shared Memory parallel programming. • Special topic: JOMP—an OpenMP-like system for Java. • Java RMI and RMI-Based Approaches for HPC • Introduction to Java RMI. • High Performance implementations of RMI. • Special topic: JavaParty—remote objects in Java. • MPI-Based Approaches for Java • Survey of MPI-like systems for Java • Programming with mpiJava • Special topic: HPJava—a data parallel extension of Java. • As another special topic we include an Introduction to Java New I/O, and if there is time may say something about Web Services, Grids, etc. dbcarpen@indiana.edu