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.