[논문 리뷰] Performance comparison between Java and JNI for optimal implementation of computational micro-kernels
이 논문은 고성능 컴퓨팅(HPC) 환경에서 JIT 컴파일과 JNI를 통한 네이티브 코드 간 성능 트레이드오프를 분석하여, 계산 집약적인 마이크로 커널을 구현하기 위해 자바와 JNI를 비교한다. JNI는 벡터화, 순서 변경 실행 등 더 나은 최적화를 통해 순수 자바보다 성능이 뛰어나지만, JNI 호출 오버헤드로 인해 호출당 연산 수가 임계값을 초과하지 않으면 성능 향상이 제한된다. 데이터 정렬 및 네이티브 메모리 접근 방식이 성능에 상당한 영향을 미친다.
General purpose CPUs used in high performance computing (HPC) support a vector instruction set and an out-of-order engine dedicated to increase the instruction level parallelism. Hence, related optimizations are currently critical to improve the performance of applications requiring numerical computation. Moreover, the use of a Java run-time environment such as the HotSpot Java Virtual Machine (JVM) in high performance computing is a promising alternative. It benefits from its programming flexibility, productivity and the performance is ensured by the Just-In-Time (JIT) compiler. Though, the JIT compiler suffers from two main drawbacks. First, the JIT is a black box for developers. We have no control over the generated code nor any feedback from its optimization phases like vectorization. Secondly, the time constraint narrows down the degree of optimization compared to static compilers like GCC or LLVM. So, it is compelling to use statically compiled code since it benefits from additional optimization reducing performance bottlenecks. Java enables to call native code from dynamic libraries through the Java Native Interface (JNI). Nevertheless, JNI methods are not inlined and require an additional cost to be invoked compared to Java ones. Therefore, to benefit from better static optimization, this call overhead must be leveraged by the amount of computation performed at each JNI invocation. In this paper we tackle this problem and we propose to do this analysis for a set of micro-kernels. Our goal is to select the most efficient implementation considering the amount of computation defined by the calling context. We also investigate the impact on performance of several different optimization schemes which are vectorization, out-of-order optimization, data alignment, method inlining and the use of native memory for JNI methods.
연구 동기 및 목표
- 핫스팟 JVM 환경에서 고성능 컴퓨팅(HPC)을 위한 계산 마이크로 커널의 최적 구현 방식(순수 자바 대비 JNI)을 규명하기 위해.
- JIT 컴파일의 제한(통제 불가, 최적화 감소)과 JNI의 높은 호출 오버헤드이지만 더 깊은 정적 최적화 간의 성능 트레이드오프를 분석하기 위해.
- 산술 강도, 벡터화, 순서 변경 실행, 데이터 정렬, 메모리 접근 방식(힙 대비 네이티브) 등 요소들이 성능에 미치는 영향을 정량화하기 위해.
- JNI가 순수 자바보다 더 나은 최적화 기회를 제공함에 따라 성능이 우월해지는 호출당 연산 수(flop-per-invocation)의 임계값을 규명하기 위해.
제안 방법
- AVX 지원이 있는 x86-64 샌디 브리지 시스템에서 수치적 마이크로 커널(예: 배열 덧셈, 수평 합, 호너 법칙)을 벤치마크하여 성능을 측정했다.
- 순수 자바, 힙 메모리 사용 시 JNI, 네이티브 메모리 사용 시 JNI, 다양한 최적화 수준을 포함한 여러 구성에서 flop/s 단위로 성능을 측정했다.
- 산술 강도(AI = 호출당 flop 수 / 호출당 메모리 사용량)를 사용하여 커널을 메모리 제한 또는 CPU 제한으로 분류했다.
- JNI 코드에 대해 정적 컴파일(GCC)을 적용하고, 벡터화, 순서 변경 실행, 인라인화 영향을 분석했다.
- 배열 정렬 방식(8바이트 대비 16바이트)을 변경하여 데이터 정렬 영향을 평가하고 성능 저하를 측정했다.
- 인라인화를 비활성화하여 JNI와 자바 메서드 호출 비용을 비교함으로써 호출 오버헤드를 측정했다.
실험 결과
연구 질문
- RQ1마이크로 커널에서 JNI가 순수 자바를 초월하는 데 필요한 호출당 연산 수(flop-per-invocation)의 임계값은 얼마인가?
- RQ2네이티브 코드에서의 벡터화 및 순서 변경 실행은 JIT 컴파일된 자바 코드 대비 성능을 얼마나 향상시키는가?
- RQ3데이터 비정렬이 자바 및 JNI 환경의 벡터화 마이크로 커널에 미치는 성능 영향은 어떠한가?
- RQ4힙 메모리 대비 네이티브 메모리 사용이 JNI 호출 오버헤드와 성능에 어떤 영향을 미치는가?
- RQ5메서드 인라인화가 JNI 메서드 호출의 성능 저하를 얼마나 줄이는가?
주요 결과
- L3 캐시 크기 이하의 호출당 메모리 사용을 가진 마이크로 커널에서 JNI는 효과적인 벡터화와 순서 변경 실행 최적화 덕분에 순수 자바 대비 최대 2배의 속도 향상을 기록한다.
- 호너 법칙 데이터-1순서 커널의 경우, JNI에서의 벡터화와 순서 변경 실행을 조합함으로써 CPU 최고 성능에 도달했지만, JIT는 이러한 패턴을 벡터화하지 못했다.
- 배열 덧셈 커널에서 한 배열은 8바이트 정렬, 다른 배열은 16바이트 정렬일 경우 비정렬 메모리 접근으로 인해 성능이 약 45% 저하되었다.
- JNI 호출 오버헤드의 주요 원인은 힙 메모리 접근이다: 자바 힙에서 배열 포인터를 가져오고 해제하는 데 약 40%의 성능 저하가 발생하며, 네이티브 메모리 사용 시 이 오버헤드가 감소한다.
- 자바 메서드의 인라인화를 비활성화하면 성능이 약 20% 감소하여, 인라인화가 메서드 호출 비용을 상당히 완화함을 시사한다.
- 저메모리 마이크로 커널의 경우 성능 저하는 호출 기반이며, 일정한 루프 비용이 지배적이다. 이 오버헤드는 JNI의 콜백 오버헤드로 인해 더욱 심화된다.
더 나은 연구,지금 바로 시작하세요
논문 읽기부터 검토까지, 연구 시간을 획기적으로 줄여보세요.
카드 등록 없음 · 무료 플랜 제공
이 리뷰는 AI가 만들고, 인간 에디터가 검토했습니다.