LordJoe Home
Comments
Native Methods



Native Methods Java and C++ Files with Projects

Basic Objective
   This sample shows two ways to multiply a matrix - one is written in Java, the other uses a JNI Native call. Rather than dealing with the object directly, the square matrix is reduced to an integer dimension and an array of doubles before the native call. It is highly recommended that interested parties download the full code and projects.

   It is an interesting exercise (which I have not done) to test speed of Java and native methods.

    //
    // implementation of multiplyMatrix - we extract data and
    // call a native method - then construct a matrix to return
    public Matrix multiplyMatrix(Matrix M1,Matrix M2) {
        int D1 = M1.getDimension();
        double[] data = nativeMultiply( D1,M1.getData(),M2.getData());
        return(new Matrix(D1,data));
    }

    // native declaration
    protected  native double[] nativeMultiply(int dimension,double[] data1,
        double[] data2);

==================================
C++ implementation

#include <jni.h>
#include "NativeMatrixMultiplier.h"

//
// This code implements the native call nativeMultiply
// here we pull out the dimension of a square matrix and two
// double[] arrays of vaules and pass them in. Note how this
// simplifies life compared to having to invoke the java cod as in the
// next sample.
extern "C"
JNIEXPORT jdoubleArray JNICALL Java_NativeMatrixMultiplier_nativeMultiply
  (JNIEnv *env, jobject caller, jint dimension, jdoubleArray data1,
  jdoubleArray data2)
{
  // create a new double[] object to return
     jdoubleArray ret = env->NewDoubleArray((jint)(dimension * dimension)) ;
  // get Data - no copy from the returned object we will set these values
  double *OutData = env->GetDoubleArrayElements(ret,JNI_FALSE);

  // get Data - no copy - from data1 array - these are read only
  const double *RealData1 = env->GetDoubleArrayElements(data1,JNI_FALSE);
  // get Data - no copy - from data2 array - these are read only
  const double *RealData2 = env->GetDoubleArrayElements(data2,JNI_FALSE);

  // usual matrix multiply
    for(int i = 0; i < dimension; i++) {
        for(int k = 0; k < dimension; k++) {
            double dotProduct = 0;
            for(int l = 0; l < dimension; l++) {
                dotProduct += RealData1[i + dimension * l] * RealData1[l + dimension * k];
            }
   // set the output data
            OutData[i+ dimension * k] = dotProduct;
        }
    }
 // return constructed double[]
 return(ret);
}
 
 

A More Complex Sample
   This sample passing the matrix as an object and calling Java methods from the JNI C++ code to
get the data.

    //
    // implementation of multiplyMatrix - here we pass objects directly
    // and let the native code extract the relevant data.
    // call a native method - then construct a matrix to return
    public Matrix multiplyMatrix2(Matrix M1,Matrix M2) {
         double[] data = nativeMultiply2(M1,M2);
         return(new Matrix(M1.getDimension(),data));
    }
    //
    // fancier version passing the objects not the data
    protected  native double[] nativeMultiply2(Matrix M1,Matrix M2);

 ==================================
C++ implementation

#include <jni.h>
#include "NativeMatrixMultiplier.h"

//
// This code implements the native call nativeMultiply2
// it differs from the previous code in that the java object Matrix1 and 2
// are passed in the the needed data- dimensions and double[] data are
// obtained by calling java methods
// NOTE - this is a pain in the ass - it is generally not a good design
// Also debugging will be very difficult
extern "C"
JNIEXPORT jdoubleArray JNICALL Java_NativeMatrixMultiplier_nativeMultiply2
  (JNIEnv *env , jobject caller, jobject Matrix1, jobject Matrix2)
{
 // Get the class for Matrix1
  jclass MatrixClass = env->GetObjectClass(Matrix1); // trust  Matrix2 is same

  // get method getDimension - 0 arguments return int
  jmethodID GetDimensionID = env->GetMethodID(MatrixClass,"getDimension","()I");
  // get method getData - 0 arguments return double[]
  jmethodID GetDataID      = env->GetMethodID(MatrixClass,"getData","()[D");

  // invoke the getDimension method
  jint dimension =  env->CallIntMethod(Matrix1,GetDimensionID);

  // create a new double[] object to return
  jdoubleArray ret = env->NewDoubleArray((jint)(dimension * dimension)) ;
  // get Data - no copy
  double *OutData = env->GetDoubleArrayElements(ret,JNI_FALSE);

  // get double[] data from matrix 1 and 2 by invoking getData
  jdoubleArray data1 = (jdoubleArray)env->CallObjectMethod(Matrix1,GetDataID);
  jdoubleArray data2 = (jdoubleArray)env->CallObjectMethod(Matrix2,GetDataID);

  // get Data - no copy - from data1 array - these are read only
  const double *RealData1 = env->GetDoubleArrayElements(data1,JNI_FALSE);
  // get Data - no copy - from data2 array - these are read only
  const double *RealData2 = env->GetDoubleArrayElements(data2,JNI_FALSE);

  // usual matrix multiply
    for(int i = 0; i < dimension; i++) {
        for(int k = 0; k < dimension; k++) {
            double dotProduct = 0;
            for(int l = 0; l < dimension; l++) {
                dotProduct += RealData1[i + dimension * l] * RealData1[l + dimension * k];
            }
   // set the output data
            OutData[i+ dimension * k] = dotProduct;
        }
    }
 // return constructed double[]
 return(ret);

}

 ==================================
Java implementation - for contrast

 //
// This class implements a MatrixMultiplier
// (do not ask why this is not a static method)
// it is used as a check on the native code in
// NativeMatrixMultiplier which does the same operations
public class JavaMatrixMultiplier implements MatrixMultiplier
{
    public JavaMatrixMultiplier() {}
    //
    // multiply two matrices - return the product
    public Matrix multiplyMatrix(Matrix M1,Matrix M2) {
        int D1 = M1.getDimension();
        int D2 = M2.getDimension();
        if(D1 != D2)
            throw new IllegalArgumentException("Incompatable Dimensions");

        Matrix out = new Matrix(D1);
        for(int i = 0; i < D1; i++) {
            for(int k = 0; k < D1; k++) {
                double dotProduct = 0;
                for(int l = 0; l < D1; l++) {
                    dotProduct += M1.getDataItem(i,l) * M1.getDataItem(l,k);
                }
                out.setDataItem(i,k,dotProduct);
            }
        }
        return(out);
    }
}
 
 

Home
(C) Copyright Steven Lewis, 1998.
Last updated: 6/10/98