为什么我的方法的JNI实现比纯Java运行得慢? 作为我的CS类之一,我必须在java中编写一个矩阵类,其中一些方法在java中实现,C++通过java原生接口实现,并测量执行时间的差异。
编写和调试这两个版本非常简单,在花了大约3个小时的时间在谷歌上搜索如何选择界面后,我最终得到了以下代码: Matrix.java:为什么我的方法的JNI实现比纯Java运行得慢? 作为我的CS类之一,我必须在java中编写一个矩阵类,其中一些方法在java中实现,C++通过java原生接口实现,并测量执行时间的差异。,java,c++,java-native-interface,Java,C++,Java Native Interface,编写和调试这两个版本非常简单,在花了大约3个小时的时间在谷歌上搜索如何选择界面后,我最终得到了以下代码: Matrix.java: public class Matrix { private double[] data; private int width, height; public Matrix(int h, int w) { width = w; height = h; data = new double[w *
public class Matrix {
private double[] data;
private int width, height;
public Matrix(int h, int w) {
width = w;
height = h;
data = new double[w * h];
}
public static void main(String[] args) {
/* takes 3 parametres u, v and w, creates two matrices m1 and m2, dimensions u*v and v*w
* fills them with random doubles, multiplies m1 * m2 with both methods
* reports time elapsed and checks equality of result */
}
public Matrix multiply(Matrix mat) { return multiply(mat, false); }
public Matrix multiplyNative(Matrix mat) { return multiply(mat, true); }
public Matrix multiply(Matrix mat, boolean natively) {
int u, v, w;
u = this.height;
w = mat.width;
Matrix res = new Matrix(u, w);
if(this.width == mat.height) v = this.width;
else return res;
if(natively) multiplyC(this.data, mat.data, res.data, u, v, w);
else {
for(int i=0; i<u; i++) {
for(int j=0; j<w; j++) {
double elem = 0.0;
for(int k=0; k<v; k++) {
elem += this.data[i*v+k] * mat.data[k*w+j];
}
res.data[i*w+j] = elem;
}
}
}
return res;
}
public static native void multiplyC(double[] a, double[] b, double[] r, int i, int j, int k);
// SNIP: equals and random-prefill methods
static {
System.loadLibrary("Matrix");
}
}
#include "Matrix.h"
JNIEXPORT void JNICALL Java_Matrix_multiplyC(JNIEnv *env, jclass,
jdoubleArray a, jdoubleArray b, jdoubleArray res,
jint u, jint v, jint w) {
jdouble* mat1 = env->GetDoubleArrayElements(a, 0);
jdouble* mat2 = env->GetDoubleArrayElements(b, 0);
jdouble* mat_res = env->GetDoubleArrayElements(res, 0);
for(int i=0; i<u; i++) {
for(int j=0; j<w; j++) {
jdouble elem = 0.0;
for(int k=0; k<v; k++) {
elem += mat1[i*v+k] * mat2[k*w+j];
}
mat_res[i*w+j] = elem;
}
}
env->ReleaseDoubleArrayElements(a, mat1, 0);
env->ReleaseDoubleArrayElements(b, mat2, 0);
env->ReleaseDoubleArrayElements(res, mat_res, 0);
}
正如您所看到的,本机版本运行所需的时间相当长,但是这两个版本的比率似乎不稳定,似乎不符合趋势,但是当我多次重新运行相同大小的版本时,它相对稳定
更奇怪的是,在我的Macbook上,它遵循一条完全不同的曲线:它的开始速度类似,小尺寸的速度要慢2倍,中等尺寸(大约100-200行/列)的速度要慢20-30%,而大尺寸的速度要慢20-30%
axim@ax1m-MBP:~/Desktop/CodeStuff/prcpp/a1/matrix$ java Matrix 5 12 8
time taken in Java: 32454ns
time taken in C++: 43379ns
results equal: true
axim@ax1m-MBP:~/Desktop/CodeStuff/prcpp/a1/matrix$ java Matrix 20 48 32
time taken in Java: 1278592ns
time taken in C++: 103246ns
results equal: true
axim@ax1m-MBP:~/Desktop/CodeStuff/prcpp/a1/matrix$ java Matrix 80 192 128
time taken in Java: 12594845ns
time taken in C++: 2604591ns
results equal: true
axim@ax1m-MBP:~/Desktop/CodeStuff/prcpp/a1/matrix$ java Matrix 320 768 512
time taken in Java: 1272993352ns
time taken in C++: 1217730765ns
results equal: true
axim@ax1m-MBP:~/Desktop/CodeStuff/prcpp/a1/matrix$ java Matrix 1280 3072 2048
time taken in Java: 110882859155ns
time taken in C++: 102803692425ns
results equal: true
这里的第三个电话是关于我期望与同学交谈的内容,但是程序需要根据作业处理更大的数据。如果有人能解释这里到底发生了什么,那就太好了。在编译代码时尽量使用-O3;) 首先,您不必提交输入数组的更改。如果您将JNI_ABORT用于不需要传递回Java的数组,则在C++中可以获得更快的计算速度:
-O3
java -Djava.library.path=. -cp . Matrix 5 12 8
C++: 0
java -Djava.library.path=. -cp . Matrix 20 48 32
C++: 0
java -Djava.library.path=. -cp . Matrix 80 192 128
C++: 2
java -Djava.library.path=. -cp . Matrix 320 768 512
C++: 1254
java -Djava.library.path=. -cp . Matrix 1280 3072 2048
C++: 104179
-O0
java -Djava.library.path=. -cp . Matrix 5 12 8
C++: 0
java -Djava.library.path=. -cp . Matrix 20 48 32
C++: 0
java -Djava.library.path=. -cp . Matrix 80 192 128
C++: 7
java -Djava.library.path=. -cp . Matrix 320 768 512
C++: 2400
java -Djava.library.path=. -cp . Matrix 1280 3072 2048
C++: 183814
-O3 + JNI_ABORT
java -Djava.library.path=. -cp . Matrix 5 12 8
C++: 0
java -Djava.library.path=. -cp . Matrix 20 48 32
C++: 0
java -Djava.library.path=. -cp . Matrix 80 192 128
C++: 3
java -Djava.library.path=. -cp . Matrix 320 768 512
C++: 1121
java -Djava.library.path=. -cp . Matrix 1280 3072 2048
C++: 96696
Java
java -Djava.library.path=. -cp . Matrix 5 12 8
Java: 0
java -Djava.library.path=. -cp . Matrix 20 48 32
Java: 1
java -Djava.library.path=. -cp . Matrix 80 192 128
Java: 13
java -Djava.library.path=. -cp . Matrix 320 768 512
Java: 1242
java -Djava.library.path=. -cp . Matrix 1280 3072 2048
Java: 101324
您可以在此处阅读有关JNI_中止的更多信息:
如果我应该编写这个代码,我会把u,v,w传递给C++,我会在那里创建数组,我会创建输出数组并把它传给java。数据的无方式复制粘贴;) 编译代码时尽量使用-O3;) 首先,您不必提交输入数组的更改。如果您将JNI_ABORT用于不需要传递回Java的数组,则在C++中可以获得更快的计算速度:
-O3
java -Djava.library.path=. -cp . Matrix 5 12 8
C++: 0
java -Djava.library.path=. -cp . Matrix 20 48 32
C++: 0
java -Djava.library.path=. -cp . Matrix 80 192 128
C++: 2
java -Djava.library.path=. -cp . Matrix 320 768 512
C++: 1254
java -Djava.library.path=. -cp . Matrix 1280 3072 2048
C++: 104179
-O0
java -Djava.library.path=. -cp . Matrix 5 12 8
C++: 0
java -Djava.library.path=. -cp . Matrix 20 48 32
C++: 0
java -Djava.library.path=. -cp . Matrix 80 192 128
C++: 7
java -Djava.library.path=. -cp . Matrix 320 768 512
C++: 2400
java -Djava.library.path=. -cp . Matrix 1280 3072 2048
C++: 183814
-O3 + JNI_ABORT
java -Djava.library.path=. -cp . Matrix 5 12 8
C++: 0
java -Djava.library.path=. -cp . Matrix 20 48 32
C++: 0
java -Djava.library.path=. -cp . Matrix 80 192 128
C++: 3
java -Djava.library.path=. -cp . Matrix 320 768 512
C++: 1121
java -Djava.library.path=. -cp . Matrix 1280 3072 2048
C++: 96696
Java
java -Djava.library.path=. -cp . Matrix 5 12 8
Java: 0
java -Djava.library.path=. -cp . Matrix 20 48 32
Java: 1
java -Djava.library.path=. -cp . Matrix 80 192 128
Java: 13
java -Djava.library.path=. -cp . Matrix 320 768 512
Java: 1242
java -Djava.library.path=. -cp . Matrix 1280 3072 2048
Java: 101324
您可以在此处阅读有关JNI_中止的更多信息:
如果我应该编写这个代码,我会把u,v,w传递给C++,我会在那里创建数组,我会创建输出数组并把它传给java。数据的无方式复制粘贴;) >通过JNI接口C++代码,代价是一个好的java编译器可以消除。这个问题似乎更适合,尽管它有助于把它更多地表述为“寻求本地方法调用(C++实现)的性能改进,以超越java代码IIrc,当通过JNI时,数组会被复制,这可能是您的性能相关的全部原因:1)没有列出用于创建C++模块的编译器选项。我们不知道您是否启用了优化。2)你没有以孤立的方式来测量C++。C++代码中的调用在哪里启动/停止计时器,以查看实际代码的执行方式?通过JNI接口连接C++代码,代价是一个好的java编译器可以消除。这个问题似乎更适合,尽管它有助于把它更像是“寻找本地方法调用的性能改进”。(C++实现),为了超越java代码“IrrC”,当通过JNI时复制数组,这可能是执行HiTeaTrime:(1)你没有列出用于创建C++模块的编译器选项的原因。我们不知道您是否启用了优化。2)你没有以孤立的方式来测量C++。C++代码中的调用在哪里启动/停止计时器,以查看实际代码如何执行?