Reputation: 1984
I have a java program calling a JNI function to perform computation on a byte array, returning a long array of. The programs runs on Oracle JDK 1.8.0_221. To test the throughput, I run the code for multiple iterations on the same byte array of size 64M, using JMH 1.21
The java side code is as following
public long[] compute(byte[] input, int resultSize) {
long[] result = new long[resultSize];
nativeCompute(input, result);
return result;
}
native void nativeCompute(byte[] input, long[] output);
The JNI code simply obtains pointers from Java arrays and pass it to the compute function
JNIEXPORT void JNICALL Java_NativeCompute_nativeCompute
(JNIEnv *env, jobject self, jbyteArray input, jlongArray result) {
// uint8_t *data = (uint8_t *) env->GetPrimitiveArrayCritical(input, 0);
// uint64_t *localres = (uint64_t *) env->GetPrimitiveArrayCritical(result, 0);
jbyte *data = env->GetByteArrayElements(input, 0);
jlong *localres = env->GetLongArrayElements(result, 0);
compute((uint8_t *) data, (uint64_t *) localres);
// env->ReleasePrimitiveArrayCritical(input, data, JNI_ABORT);
// env->ReleasePrimitiveArrayCritical(result, localres, JNI_COMMIT);
env->ReleaseLongArrayElements(result, localres, JNI_COMMIT);
env->ReleaseByteArrayElements(input, data, JNI_ABORT);
}
I use GetByteArrayElements
and GetLongArrayElements
to fetch pointers from java array, and the program works fine.
To avoid array copy, I try to switch to GetPrimitiveArrayCritical/ReleasePrimitiveArrayCritical
, as can be seen in the code that is commented out. There is a 15% performance improvement, but JVM crashs after running for some (30-150) iterations. The error log is as below
# Run progress: 0.00% complete, ETA 00:12:30
# Fork: 1 of 5
# Warmup Iteration 1: 7.436 ops/s
# Warmup Iteration 2: #
# A fatal error has been detected by the Java Runtime Environment:
#
# SIGSEGV (0xb) at pc=0x00007fe4b14ca554, pid=14023, tid=0x00007fe49baa2700
#
# JRE version: Java(TM) SE Runtime Environment (8.0_221-b11) (build 1.8.0_221-b11)
# Java VM: Java HotSpot(TM) 64-Bit Server VM (25.221-b11 mixed mode linux-amd64 compressed oops)
# Problematic frame:
# V [libjvm.so+0x987554][thread 140620183496448 also had an error]
[thread 140619839837952 also had an error]
oopDesc* PSPromotionManager::copy_to_survivor_space<false>(oopDesc*)+0x204
#
When I switch back to Get<XX>ArrayElements
, this error is gone. But I still want to get the 15% performance improvement from GetPrimitiveArrayCritical
.
Any suggestion is appreciated. Thanks!
Thanks everyone for your suggestion! I have tried as you suggested and here's some update.
I added code to check the pointers for NULL. They are not null.
The error only occurs in JMH environment. When I run the code as standalone and repeat for 2000 iterations, there's no error.
I commented out the compute, and the error is gone. Seems like the crash is caused by array overflow. I double check the code and found that when the number of input elements is a multiple of 64, my code will accidentally cross boundary of the result array. I fix that bug and now the error is gone. I should have double checked the code to avoid this kind of silly mistake.
Although it is still not clear why the error only occurs in JMH environment, I think we have found the root cause and thank you all for the help!
Upvotes: 1
Views: 946
Reputation: 13385
In my opinion, something fishy is going o inside your compute stuff. If I do very simple test, with the very same approach you have, I don't encounter any issues.
Also, it's quite important that compute
doesn't do anything JNI
related. PrimitiveArray
comes with the price.
Of course, for 100 loops over the same function (as in here: https://github.com/mkowsiak/jnicookbook/tree/master/recipes/recipeNo053) you can see clear benefits in speedup
Access via PrimitiveArray
14.64 real 14.55 user 0.12 sys
Access via ArrayElements
51.55 real 35.94 user 15.13 sys
Two and half times faster. Anyway, as PrimitiveArray
comes with the cost
After calling GetPrimitiveArrayCritical, the native code should not run for an extended period of time before it calls ReleasePrimitiveArrayCritical.
I'd rethink whether using PrimitiveArray
for this kind of array sizes is what you really want.
Update
Ups! It looks like it was solved while I was preparing the answer ;)
Upvotes: 1