Java Native Interface JNI Phil PrattSzeliga Syracuse University

  • Slides: 23
Download presentation
Java Native Interface (JNI) Phil Pratt-Szeliga Syracuse University

Java Native Interface (JNI) Phil Pratt-Szeliga Syracuse University

JNI Overview � JNI allows a developer to interop with arbitrary C/C++ code �

JNI Overview � JNI allows a developer to interop with arbitrary C/C++ code � The managed to native transition can take time, so don’t expect a performance enhancement from, say, repeatedly incrementing an integer � With JNI you need to have a. dll built for Windows and a. so built for Linux (and I guess a. so for Mac). • Your build process needs to be able to built the. dll on Windows and the. so on Linux and then possibly pack it in the jar file • You need to build two separate dll’s and so’s to cover 32 bit and 64 bit platforms.

Calling JNI Code From Java package edu. syr. distobjects. jniexample; public class First. Example

Calling JNI Code From Java package edu. syr. distobjects. jniexample; public class First. Example { //it is good to make public wrappers around //native methods public void print. String(String str){ do. Print. String(str); } } //the native keyword it present here private native void do. Print. String(String str);

Creating the C/C++ Headers � Directory structure: • netbeans_root �build �classes � edu �syr

Creating the C/C++ Headers � Directory structure: • netbeans_root �build �classes � edu �syr � distobjects � jniexample �src � edu �syr � distobjects � jniexample �native (you need to make this directory) � Linux: • $ cd /path/to/netbeans_root/build/classes • $ javah edu. syr. distobjects. jniexample. First. Example • $ mv edu_syr_distobjects_jniexample_First. Example. h. . /native/ � Windows: • $ cd pathtonetbeans_rootbuildclasses • $ “C: Program Files (x 86)Javajkd 1. 6. 0_26binjavah” edu. syr. distobjects. jniexample. First. Example • $ move edu_syr_distobjects_jniexample_First. Example. h. . native

Created C/C++ Header /* DO NOT EDIT THIS FILE - it is machine generated

Created C/C++ Header /* DO NOT EDIT THIS FILE - it is machine generated */ #include <jni. h> /* Header for class edu_syr_distobjects_jniexample_First. Example */ #ifndef _Included_edu_syr_distobjects_jniexample_First. Example #define _Included_edu_syr_distobjects_jniexample_First. Example #ifdef __cplus extern "C" { #endif /* * Class: edu_syr_distobjects_jniexample_First. Example * Method: do. Print. String * Signature: (Ljava/lang/String; )V */ JNIEXPORT void JNICALL Java_edu_syr_distobjects_jniexample_First. Example_do. Print. String (JNIEnv *, jobject, jstring); #ifdef __cplus } #endif

Copy Prototype to. cpp File #include “edu_syr_distobjects_jniexample_First. Example. h” JNIEXPORT void JNICALL Java_edu_syr_distobjects_jniexample_First. Example_do.

Copy Prototype to. cpp File #include “edu_syr_distobjects_jniexample_First. Example. h” JNIEXPORT void JNICALL Java_edu_syr_distobjects_jniexample_First. Example_do. Print. String (JNIEnv * env, jobject this_obj, jstring str) { } � Note I gave names to env, this_obj, � env: Every JNI function is passed a and str. JNIEnv pointer � this_obj: Represents the managed “this” pointer � str: The only argument to the method in the Java code

Implement a cout #include “edu_syr_distobjects_jniexample_First. Example. h” #include <iostream> JNIEXPORT void JNICALL Java_edu_syr_distobjects_jniexample_First. Example_do.

Implement a cout #include “edu_syr_distobjects_jniexample_First. Example. h” #include <iostream> JNIEXPORT void JNICALL Java_edu_syr_distobjects_jniexample_First. Example_do. Print. String (JNIEnv * env, jobject this_obj, jstring str) { int len = (*env)->Get. String. Length(env, str); char * nstr = new char[len]; (*env)->Get. String. UTFRegion(env, str, 0, len, nstr); std: : cout << nstr << std: : endl; delete [] nstr; }

Build the. dll � Put this in cl_options. txt (on one line) /I"C: Program

Build the. dll � Put this in cl_options. txt (on one line) /I"C: Program FilesJavajdk 1. 6. 0_26include“ /I"C: Program FilesJavajdk 1. 6. 0_26includewin 32“ First. Example. cpp /DLL /OUT: first_example. dll /MACHINE: X 64 � Make a native_build. bat (keep lines separate) "C: Program Files (x 86)Microsoft Visual Studio 10. 0VCvcvarsall. bat" amd 64 cl @cl_options. txt

Load the. dll in Java package edu. syr. distobjects. jniexample; import java. io. File;

Load the. dll in Java package edu. syr. distobjects. jniexample; import java. io. File; public class First. Example { //called during first initialization of First. Example class static { //load requires an absolute path and this method ensures that File file = new File(“first_example. dll”); System. load(file. get. Absolute. Path()); } public void print. String(String str){ do. Print. String(str); } } private native void do. Print. String(String str);

Distributing the. dll with a. jar � In netbeans if you create a package

Distributing the. dll with a. jar � In netbeans if you create a package (folder) and then have your build process place the. dll in the folder, it will be included in the. jar public class Binary. Loader { public void extract. Binary(String filename) throws Exception { String path = "/edu/syr/distobjects/jniexample/native/"+filename; Input. Stream is = Binary. Loader. class. get. Resource. As. Stream(path); Output. Stream os = new File. Output. Stream(filename); while(true){ byte[] buffer = new byte[32*1024]; int len = is. read(buffer); if(len == -1) { break; } os. write(buffer, 0, len); } os. flush(); os. close(); is. close(); } }

Saving State In a cpp �You can save state in a high performance way

Saving State In a cpp �You can save state in a high performance way by using static globals in a cpp • But all instances of a class will share that state �A slower way is to save state back to a Java Field void set. Long. Field(JNIEnv * env, jobject obj, long value){ jclass this_class = (*env)->Get. Object. Class(env, obj); jfield. ID fid = (*env)->Get. Field. ID(env, this_class, “m_Field. Name”, "J"); (*env)->Set. Long. Field(env, obj, fid, value); }

Java Type Strings � Previously we passed in “J” into Get. Field. ID •

Java Type Strings � Previously we passed in “J” into Get. Field. ID • B = byte • C = char • D = double • F = float • I = int • J = long • S = short • V = void • Z = boolean • Ljava/lang/String; = String • Ljava/lang/Object; = Object • Lfully/qualified/Classname = fully. qualified. Classsname • [B – single dimensional byte array • [[B – two dimensional byte array • [Ljava/lang/String; - one dimensional string array

Calling a Java Method jbyte. Array list_get(JNIEnv * env, jobject list, int index){ jmethod.

Calling a Java Method jbyte. Array list_get(JNIEnv * env, jobject list, int index){ jmethod. ID mid; jclass list_interface = (*env)->Find. Class(env, "java/util/List"); mid = (*env)->Get. Method. ID(env, list_interface, "get", "(I)Ljava/lang/Object; "); return (*env)->Call. Object. Method(env, list, mid, index); } � “java/util/List” – interface or class name � “get” – method name � “(I)Ljava/lang/Object; ” – a method that accepts an int as a parameter and returns a Ljava/lang/Object; � jbyte. Array – a byte[] � In Java, the list was declared as: • List<byte[]> list;

JNI Field Functions � Based on Field Type: • Get. Object. Field • Get.

JNI Field Functions � Based on Field Type: • Get. Object. Field • Get. Boolean. Field • Get. Byte. Field • Get. Char. Field • Get. Short. Field • Get. Int. Field • Get. Long. Field • Get. Float. Field • Get. Double. Field � Corresponding • Set. Object. Field setters are like:

JNI Method Invocation Functions � Based on return type: • Call. Object. Method •

JNI Method Invocation Functions � Based on return type: • Call. Object. Method • Call. Boolean. Method • Call. Byte. Method • Call. Char. Method • Call. Short. Method • Call. Int. Method • Call. Long. Method • Call. Float. Method • Call. Double. Method • Call. Void. Method

JNI Array Functions � Based on Array Type: • Get. Boolean. Array. Elements •

JNI Array Functions � Based on Array Type: • Get. Boolean. Array. Elements • Get. Byte. Array. Elements • Get. Char. Array. Elements • Get. Short. Array. Elements • Get. Int. Array. Elements • Get. Long. Array. Elements • Get. Float. Array. Elements • Get. Double. Array. Elements � Corresponding releases need to be called like: • Release. Boolean. Array. Elements � Array length: • Get. Array. Length � Arrays of objects: can only get one at a time because you can’t know the size of an object to store in a regular C style array • Get. Object. Array. Element • Set. Object. Array. Element

Example of JNI Array Copy JNIEXPORT void JNICALL Java_edu_syr_pcpratts_rootbeer_Array. Memory_do. Write. Int. Array (JNIEnv

Example of JNI Array Copy JNIEXPORT void JNICALL Java_edu_syr_pcpratts_rootbeer_Array. Memory_do. Write. Int. Array (JNIEnv *env, jobject this_obj, jint. Array array, jlong ref, jint len){ char * dest = (char *) ref; jint * narray = (*env)->Get. Int. Array. Elements(env, array, JNI_FALSE); memcpy(dest, narray, len*sizeof(int)); (*env)->Release. Int. Array. Elements(env, array, narray, JNI_ABORT); } � jlong ref was previously allocated in the C++ code using new. You can easily save any pointer in C++ in a Java field using a long. ref was saved as a member field in the Java class. � JNI_FALSE and JNI_ABORT have to deal with memory pinning and copying back of the change array into the java runtime. I can’t find a reference right now for these

Real World Usage of JNI �Rootbeer: tool to make it easier to program GPUs

Real World Usage of JNI �Rootbeer: tool to make it easier to program GPUs from Java • All the GPU vendors give C bindings to their API. • API allows to: �Create/Destroy GPU memory �Copy from CPU memory to GPU memory �Query how many sub-processors a GPU has �Compile CUDA programs to a binary for the GPU �Launch jobs onto the GPU

GPUs � GPU = Graphics Processing Unit • Specialized processor originally made only to

GPUs � GPU = Graphics Processing Unit • Specialized processor originally made only to feed Monitor with byte buffer • A device has on the order of 512 cores! • Each core is quite simple and slow: �No branch prediction or out of order execution �Clock rate is 1. 3 Ghz range • Groups of 32 cores all have to be doing the same thing �They share instruction fetch hardware • Really hard to program �Need to learn special programming language to execute on the GPU (CUDA) �Need to manually serialize all of your complex classes in C/C++ to arrays (this may have changed recently…need to check) �Need to find huge amounts of parallelism in original program to get a speedup �Getting a speedup is the only reason to use a GPU �Naïve Dense matrix multiplication can easily be sped up 100 X

Rootbeer �Rootbeer allows the developer to program in Java and it automatically: • Creates

Rootbeer �Rootbeer allows the developer to program in Java and it automatically: • Creates a CUDA program from analyzing Java Bytecode (with the help of the Soot Java Optimization Framework) • Creates Java Bytecode that can (de)serialize CPU memory to GPU memory in a high performance manner • Gives a Java interface for automatically launching these jobs on the GPU

Rootbeer Status �By the end of summer 2012 there will be a public release

Rootbeer Status �By the end of summer 2012 there will be a public release of Rootbeer available for people to use • We currently have a non-optimized version that is highly tested: � 20 K SLOC product code � 5 k SLOC test code �All tests pass covering all aspects of the Java programming language except: �Sleeping while inside a monitor �Reflection �Dynamic methods in Java (recently added to Java, makes static analysis really hard) �Java Code that uses JNI… �Garbage collection

Questions?

Questions?

References �http: //dev. kanngard. net/Permalinks/ID_200 50509144235. html �http: //docs. oracle. com/javase/1. 4. 2/docs/g uide/jni/spec/functions.

References �http: //dev. kanngard. net/Permalinks/ID_200 50509144235. html �http: //docs. oracle. com/javase/1. 4. 2/docs/g uide/jni/spec/functions. html �http: //java. sun. com/docs/books/jni/html/jni. T OC. html