Hi there,
I'm looking for a fast way to alter C data from Java, let's say I've some huge char[] in C, and I'd like read / write it from Java.
I use to access that kind of byte[] using GetByteArrayElements or their Critical counterpart, though, I'd like to find a better, if possible...
copy-less way :-)
After reading a few articles, my conclusion was to give a try to NewDirectByteBuffer.
So I did, I wrote some test cases to compare :
1 ) read / write in Java with a byte[] (without copying the data from/to C++)
public static void timeByteArray(int iteration) {
byte[] b = new byte[512];
long start = System.nanoTime();
for (int j = 0; j < iteration; ++j)
for (int i = 0; i < 512; ++i) {
b[(i + 1) % 512] = b;
}
System.out.println("timeByteArray : " + ((System.nanoTime() - start) / 1000000.0f) + "ms");
}No surprise this is the fastest as there's no copy to/from JNI, this was just to get a reference time.
2 ) read / write in Java with a DirectByteBuffer created through JNI's NewDirectByteBuffer
public static void timeNioDirectByteBuffer(int iteration) {
ByteBuffer b = ByteBuffer.allocateDirect(512);
long start = System.nanoTime();
for (int j = 0; j < iteration; ++j)
for (int i = 0; i < 512; ++i) {
b.put((i + 1) % 512, b.get(i));
}
System.out.println("timeNioDirectByteBuffer : " + ((System.nanoTime() - start) / 1000000.0f) + "ms");
}Good surprise to me, it has only a ~10% hit over the byte[] (given my test and environment)
3 ) read / write in Java with a byte[] kept in sync (copied in/out after every batch of changes) with a DirectByteBuffer created through JNI's NewDirectByteBuffer
public static void timeJNIDirectBufferWithCopies(int iteration) {
Ipc ipc = new Ipc();
byte[] b = new byte[512];
ByteBuffer buffer = ipc.createBuffer(512);
long start0 = System.nanoTime();
for (int j = 0; j < iteration; ++j) {
buffer.position(0);
buffer.get(b, 0, 512);
for (int i = 0; i < 512; ++i) {
b[(i + 1) % 512] = b[i];
}
buffer.position(0);
buffer.put(b, 0, 512);
}
System.out.println("timeJNIDirectBufferWithCopies : " + ((System.nanoTime() - start0) / 1000000.0f) + "ms");
}This test was almost as good as the #2 solution, given the copies were not to frequent
4 ) read / write in Java with a DirectByteBuffer created through ByteBuffer.allocateDirect
public static void timeJNIDirectBuffer(int iteration) {
Ipc ipc = new Ipc();
ByteBuffer buffer = ipc.createBuffer(512);
long start0 = System.nanoTime();
for (int j = 0; j < iteration; ++j)
for (int i = 0; i < 512; ++i) {
buffer.put((i + 1) % 512, buffer.get(i));
}
System.out.println("timeJNIDirectBuffer : " + ((System.nanoTime() - start0) / 1000000.0f) + "ms");
}At last, the problem... when I use a DirectBuffer I created in JNI, the hit is over 200%
My tests were repeated enough to get the JIT do its work, and I took my samples after that.
Is there anything special about how to allocate the buffer ? For my test, I simply used a staticaly allocated char[], I guess it would be hard to make it simplier....
unsigned char data[512];
JNIEXPORT jobject JNICALL Java_Ipc_createBuffer(JNIEnv * env, jobject this, jlong size)
{
return env->NewDirectByteBuffer(data, 512);
}Any idea of why there could be such a performance hit ?
Please, note this is a test / proof-of-concept, I'd appreciate if the comments are about my question and not the code quality.
Thanks