JNI 호출이 느려지는 이유는 무엇입니까?


194

Java에서 JNI 호출을 할 때 '교차 경계'가 느리다는 것을 알고 있습니다.

그러나 느리게 만드는 것이 무엇인지 알고 싶습니다 . JNI 호출을 수행 할 때 기본 jvm 구현이 느리게 만드는 것은 무엇입니까?


2
(+1) 좋은 질문입니다. 우리가 주제에있는 동안, 실제 벤치 마크를 수행 한 모든 사람이 결과를 게시하도록 권장하고 싶습니다.
NPE

2
JNI 호출은 전달 된 Java 오브젝트를 예를 들어 C가 이해할 수있는 것으로 변환해야합니다. 반환 값과 동일합니다. 유형 변환과 호출 스택 마샬링은 그에 대한 좋은 덩어리입니다.
Dave Newton

데이브, 나는 전에 이해하고 들었습니다. 그러나 전환은 정확히 무엇 입니까? 그게 뭐야? 자세한 내용을 찾고 있습니다.
pdeva

직접 ByteBuffer를 사용하여 Java와 C간에 데이터를 전달하면 오버 헤드가 상대적으로 낮을 수 있습니다.
Peter Lawrey

6
호출에는 적절한 C 스택 프레임이 필요하고 모든 유용한 CPU 레지스터를 푸시하고 (그리고 다시 팝) 호출에는 펜싱이 필요하며 인라인과 같은 많은 최적화가 방지됩니다. 또한 스레드는 실행 스택 잠금 (예 : 기본 코드에서 바이어스 된 잠금이 작동하도록 허용)을 그대로두고 다시 가져와야합니다.
bestsss

답변:


174

먼저, "느리게"함으로써 수십 나노초가 걸릴 수있는 것에 대해 이야기하고 있습니다. 사소한 기본 방법의 경우 2010 년에 Windows 데스크톱에서 평균 40ns, Mac 데스크톱에서 11ns의 통화를 측정했습니다. 전화 를 많이 하지 않으면 눈치 채지 못할 것입니다.

즉, 기본 메소드 호출 은 일반 Java 메소드 호출보다 느릴 수 있습니다 . 원인은 다음과 같습니다.

  • 기본 메소드는 JVM에 의해 인라인되지 않습니다. 이 특정 머신에 대해 적시에 컴파일되지도 않으며 이미 컴파일되어 있습니다.
  • Java 배열은 원시 코드로 액세스하기 위해 복사 한 후 나중에 다시 복사 할 수 있습니다. 비용은 어레이 크기에서 선형 일 수 있습니다. Windows 데스크톱에서는 평균 약 75 마이크로 초, Mac에서는 82 마이크로 초로 100,000 어레이의 JNI 복사 를 측정했습니다 . 다행히도 직접 액세스는 GetPrimitiveArrayCritical 또는 NewDirectByteBuffer 를 통해 얻을 수 있습니다 .
  • 메소드에 객체가 전달되거나 콜백이 필요한 경우 기본 메소드가 JVM을 자체적으로 호출합니다. 원시 코드에서 Java 필드, 메소드 및 유형에 액세스하려면 리플렉션과 유사한 것이 필요합니다. 서명은 문자열로 지정되고 JVM에서 쿼리됩니다. 이 느린 둘 다 오류가 발생하기 쉬운.
  • Java 문자열은 객체이며 길이가 있으며 인코딩됩니다. 문자열에 액세스하거나 문자열을 작성하려면 O (n) 사본이 필요할 수 있습니다.

날짜에 대한 추가 논의는 "9.2 : JNI 비용 조사"의 Steve Wilson과 Jeff Kesselman의 "Java 플랫폼 성능 : 전략 및 전술"(2000)에서 찾을 수 있습니다. 아래 @Philip의 의견에 제공된 이 페이지 의 1/3 정도입니다 .

2009 IBM developerWorks 백서 "Java Native Interface 사용을위한 우수 사례" 에서는 JNI의 성능 저하를 피하는 방법에 대한 제안을 제공합니다.


1
이 답변 은 일부 원시 코드 JVM에 의해 인라인 될 있다고 주장 합니다.
AH

5
이 답변은 JNI를 사용하지 않고 일부 표준 네이티브 코드가 JVM에 인라인되어 있음을 지적합니다. 위의 "네이티브 메소드"는 JNI를 통해 구현 된 사용자 정의 네이티브 메소드의 일반적인 경우를 말합니다. sun.misc.Unsafe에 대한 포인터 주셔서 감사합니다.
Andy Thomas

이 접근법은 모든 JNI 호출에 사용될 수 있다고 주장하고 싶지 않았습니다. 그러나 순수한 바이트 코드와 순수한 JNI 코드 사이 에는 중간 지점 있다는 것을 아는 것은 아프지 않습니다 . 아마도 이것은 일부 디자인 결정에 영향을 줄 것입니다. 아마도이 메커니즘은 미래에 일반화 될 것입니다.
AH

3
@AH, 당신은 JNI로 본질적인 것을 착각합니다. 그들은 상당히 다릅니다. JVM에서 '매직'을 통해 처리되는 sun.misc.Unsafe것과 같은 다른 많은 것들이 System.currentTimeMillis/nanoTime있습니다. 그들은 JNI가 아니며 적절한 .c / .h 파일이 없으므로 JVM impl 자체를 차단합니다. JVM을 쓰거나 해킹하지 않으면 접근 방식을 따를 수 없습니다.
bestsss

1
" 이 java.sun.com 문서 "는 현재 고장났습니다. 여기 에 작동하는 링크가 있습니다.
Philip Guin

25

표시된 모든 Java 메소드 native가 "느리게" 있는 것은 아닙니다 . 그들 중 일부는 본질적으로 매우 빠릅니다. 고유하고 사람이되지 않는, 당신이 볼 수있는 사람을 확인하려면 do_intrinsic에서 vmSymbols.hpp .


23

기본적으로 JVM은 해석 적으로 각 JNI 호출에 대한 C 매개 변수를 구성하며 코드는 최적화되지 않습니다.

이 백서 에 더 많은 세부 정보가 있습니다.

JNI와 네이티브 코드의 벤치마킹에 관심이 있다면 이 프로젝트 에는 벤치 마크를 실행하기위한 코드가 있습니다.


2
연결 한 논문은 JNI의 내부 작동 방식을 설명하는 논문보다 성능 벤치 마크 논문처럼 보입니다.
pdeva

@pdeva 불행히도 내가 찾은 다른 자원은 java.sun.com에 링크되었으며 Oracle 인수 이후 링크가 업데이트되지 않았습니다. JNI 내부에 대한 자세한 내용을 찾고 있습니다.
dmck

13
이 논문은 꽤 오래 전에 Java 1.3에 관한 것입니다. 그 당시의 문제는 여전히 Java 7에도 적용됩니까?
AH
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.