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