2017-10-02 11 views
1

내 CS 클래스 중 하나의 일부로 Java 기본 인터페이스를 통해 Java 및 C++로 구현 된 일부 메서드를 사용하여 Java에서 행렬 클래스를 작성하고 실행 시간의 차이를 측정해야합니다.내 메소드의 JNI 구현이 순수 Java보다 느리게 실행되는 이유는 무엇입니까?

작성하고 두 버전을 디버깅하는 것은 충분히 간단 약 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 * 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"); 
    } 
} 

Matrix.cpp :

#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); 
} 

Howev 어쨌든 Java 구현은 대부분의 입력 크기에서 빠르거나 빠르며 일부 급우와 이야기 한 후에는 예상 결과가 아닙니다. 그러나

[email protected]:~/Desktop/prcpp/jni$ java -Djava.library.path=. Matrix 5 12 8 
time taken in Java: 11452ns 
time taken in C++: 20990ns 
results equal:  true 
[email protected]:~/Desktop/prcpp/jni$ java -Djava.library.path=. Matrix 20 48 32 
time taken in Java: 5439887ns 
time taken in C++: 5492423ns 
results equal:  true 
[email protected]:~/Desktop/prcpp/jni$ java -Djava.library.path=. Matrix 80 192 128 
time taken in Java: 19726130ns 
time taken in C++: 25375681ns 
results equal:  true 
[email protected]:~/Desktop/prcpp/jni$ java -Djava.library.path=. Matrix 320 768 512 
time taken in Java: 194357345ns 
time taken in C++: 384648461ns 
results equal:  true 
[email protected]:~/Desktop/prcpp/jni$ java -Djava.library.path=. Matrix 1280 3072 2048 
time taken in Java: 58514495266ns 
time taken in C++: 116695035710ns 
results equal:  true 

당신이 그것을 실행할 수있는 기본 버전에 소요되는 시간을 볼 수 있듯이 매우 일관되게 이상, 다음은

내 데비안 가상 상자에서 가져온 다른 매트릭스 크기에 대한 몇 가지 예제 출력 데이터이며, 비율은 불규칙한 것처럼 보이고 추세를 따르지 않는 것처럼 보이지만 동일한 크기를 여러 번 실행하면 비교적 안정적입니다.

내 MacBook의 경우 완전히 다른 곡선을 그리며 다음과 같이 완전히 다른 곡선을 그리게됩니다. 비슷한 크기로 작은 크기의 경우 약 2 배 느리게 시작되고 중간 크기 (약 100-200 줄/시간의 30 %, 그리고 큰 크기로 다시 목과 목입니다. 여기

[email protected]:~/Desktop/CodeStuff/prcpp/a1/matrix$ java Matrix 5 12 8 
time taken in Java:  32454ns 
time taken in C++:  43379ns 
results equal:   true 
[email protected]:~/Desktop/CodeStuff/prcpp/a1/matrix$ java Matrix 20 48 32 
time taken in Java:  1278592ns 
time taken in C++:  103246ns 
results equal:   true 
[email protected]:~/Desktop/CodeStuff/prcpp/a1/matrix$ java Matrix 80 192 128 
time taken in Java:  12594845ns 
time taken in C++:  2604591ns 
results equal:   true 
[email protected]:~/Desktop/CodeStuff/prcpp/a1/matrix$ java Matrix 320 768 512 
time taken in Java:  1272993352ns 
time taken in C++:  1217730765ns 
results equal:   true 
[email protected]:~/Desktop/CodeStuff/prcpp/a1/matrix$ java Matrix 1280 3072 2048 
time taken in Java:  110882859155ns 
time taken in C++:  102803692425ns 
results equal:   true 

세 번째 호출은 내 친구들에게 얘기에서 기대 한 것에 대해,하지만 프로그램은 할당에 따라 더 큰 데이터를 처리해야합니다. 도대체 무슨 일이 일어 났는지 설명 할 수 있다면 좋겠지?

+0

jni를 통해 C++ 코드를 인터페이싱하면 좋은 Java 컴파일러가 제거 할 수있는 비용이 발생합니다. – user0042

+0

이 질문은 http://codereview.stackexchange.com/에 더 잘 맞는 것처럼 보입니다. "네이티브 메서드 호출 (C++ 구현)의 성능 향상을 추구하고 Java 코드 " – Justin

+0

IIRC의 경우 JNI를 통과 할 때 배열이 복사되므로 성능이 저하 될 수 있습니다. – Justin

답변

0

이 코드를 컴파일하는 동안 -O3을 사용하려고) 모든

첫째, 당신은 입력 배열에 대한 변경 사항을 적용 할 필요가 없습니다.

-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_ABORT에 대한 자세한 내용을보실 수 있습니다 : 당신이 다시 자바로 전달 될 필요가 없습니다 배열에 대한 JNI_ABORT를 사용하면 C++에서 빠른 계산을 얻을 것이다 http://jnicookbook.owsiak.org/recipe-No-013/

나는 가정 한 경우 이 코드를 작성하려면, 저는 U, V, W를 C++에 전달했을 것입니다. 거기에 배열을 만들었을 것이고, 출력 배열을 생성하고 다시 Java로 전달할 것입니다. 덜 복사 - 데이터 붙여 넣기)