Attached is a patch that changes the native interface of VMChannel, and
allows the VM to change the way FileInputStream and FileOutputStreams
allocate ByteBuffers.

With a suitably written VMChannel.java, this achieves a 40% speedup on
DaCapo xalan on JikesRVM's 'production' configuration (less on SemiSpace
and more on MarkSweep).

One option I looked at was avoiding using a ByteBuffer altogether, passing
a byte array, offset and length right the way through the native
interface, but the speedup was negligible (I think the JIT actually
inlines and eventually optimizes away the calls to position() and limit()
on the buffer).

for consideration and comment.

regards,
Robin
diff -r -N -w -u -I '[$]Id:' --exclude='*.class' --exclude='.*' --exclude='*.orig' --exclude='*.rej' --exclude=CVS --exclude='#*' --exclude='*~' ./java/io/FileInputStream.java ./java/io/FileInputStream.java
--- ./java/io/FileInputStream.java	2006-10-05 12:44:24.000000000 +1000
+++ ./java/io/FileInputStream.java	2007-02-02 18:25:51.000000000 +1100
@@ -39,6 +39,7 @@
 package java.io;
 
 import gnu.java.nio.FileChannelImpl;
+import gnu.java.nio.VMChannel;
 
 import java.nio.ByteBuffer;
 import java.nio.channels.FileChannel;
@@ -280,7 +281,7 @@
         || offset + len > buf.length)
       throw new ArrayIndexOutOfBoundsException();
 
-    return ch.read(ByteBuffer.wrap(buf, offset, len));
+    return ch.read(VMChannel.wrap(buf,offset,len));
   }
 
   /**
diff -r -N -w -u -I '[$]Id:' --exclude='*.class' --exclude='.*' --exclude='*.orig' --exclude='*.rej' --exclude=CVS --exclude='#*' --exclude='*~' ./java/io/FileOutputStream.java ./java/io/FileOutputStream.java
--- ./java/io/FileOutputStream.java	2006-10-05 12:44:24.000000000 +1000
+++ ./java/io/FileOutputStream.java	2007-02-02 18:21:56.000000000 +1100
@@ -39,6 +39,7 @@
 package java.io;
 
 import gnu.java.nio.FileChannelImpl;
+import gnu.java.nio.VMChannel;
 
 import java.nio.ByteBuffer;
 import java.nio.channels.FileChannel;
@@ -280,7 +281,8 @@
         || offset + len > buf.length)
       throw new ArrayIndexOutOfBoundsException ();
     
-    ch.write(ByteBuffer.wrap(buf, offset, len));
+    //ch.write(buf, offset, len);
+    ch.write(VMChannel.wrap(buf, offset, len));
   }
 
   /**
diff -r -N -w -u -I '[$]Id:' --exclude='*.class' --exclude='.*' --exclude='*.orig' --exclude='*.rej' --exclude=CVS --exclude='#*' --exclude='*~' ./native/jni/java-nio/gnu_java_nio_VMChannel.c ./native/jni/java-nio/gnu_java_nio_VMChannel.c
--- ./native/jni/java-nio/gnu_java_nio_VMChannel.c	2006-12-05 09:17:12.000000000 +1100
+++ ./native/jni/java-nio/gnu_java_nio_VMChannel.c	2007-01-31 13:53:20.000000000 +1100
@@ -79,6 +79,8 @@
 #define ALIGN_DOWN(p,s) ((p) - ((p) % (s)))
 #define ALIGN_UP(p,s) ((p) + ((s) - ((p) % (s))))
 
+#define NIODBG printf
+
 /*
  * Limit to maximum of 16 buffers
  */
@@ -104,7 +106,9 @@
 jmethodID get_method_id(JNIEnv *, jclass, const char *, const char *);
 void JCL_print_buffer(JNIEnv *, struct JCL_buffer *);
 int JCL_init_buffer(JNIEnv *, struct JCL_buffer *, jobject);
+int JCL_init_buffer_fast(JNIEnv *, struct JCL_buffer *, jobject, int, int);
 void JCL_release_buffer(JNIEnv *, struct JCL_buffer *, jobject, jint);
+void JCL_release_buffer_fast(JNIEnv *, struct JCL_buffer *, jobject, jint);
 void JCL_cleanup_buffers(JNIEnv *, struct JCL_buffer *, jint, jobjectArray, jint, jlong);
 int JCL_thread_interrupted(JNIEnv *);
 
@@ -144,12 +148,19 @@
 int
 JCL_init_buffer(JNIEnv *env, struct JCL_buffer *buf, jobject bbuf)
 {
+  return JCL_init_buffer_fast(env,buf,bbuf,(*env)->CallIntMethod(env, bbuf, get_position_mid),
+    (*env)->CallIntMethod(env, bbuf, get_limit_mid));
+}
+
+int
+JCL_init_buffer_fast(JNIEnv *env, struct JCL_buffer *buf, jobject bbuf, int pos, int limit)
+{
   void *addr = (*env)->GetDirectBufferAddress (env, bbuf);
 
 /*   NIODBG("buf: %p; bbuf: %p; addr: %p", (void *) buf, bbuf, addr); */
   
-  buf->position = (*env)->CallIntMethod(env, bbuf, get_position_mid);
-  buf->limit = (*env)->CallIntMethod(env, bbuf, get_limit_mid);
+  buf->position = pos;
+  buf->limit = limit;
   buf->offset = 0;
   buf->count = 0;
   buf->type = UNKNOWN;
@@ -191,8 +202,6 @@
 JCL_release_buffer(JNIEnv *env, struct JCL_buffer *buf, jobject bbuf, 
     jint action)
 {
-  jbyteArray arr;
-
 /*   NIODBG("buf: %p; bbuf: %p; action: %x", (void *) buf, bbuf, action); */
   
   /* Set the position to the appropriate value */
@@ -203,6 +212,16 @@
                                           buf->position + buf->count);
       (*env)->DeleteLocalRef(env, bbufTemp);
     }
+  JCL_release_buffer_fast(env,buf,bbuf,action);   
+}
+
+void
+JCL_release_buffer_fast(JNIEnv *env, struct JCL_buffer *buf, jobject bbuf, 
+    jint action)
+{
+  jbyteArray arr;
+
+/*   NIODBG("buf: %p; bbuf: %p; action: %x", (void *) buf, bbuf, action); */
     
   switch (buf->type)
     {
@@ -374,31 +393,30 @@
 
 
 JNIEXPORT jint JNICALL 
-Java_gnu_java_nio_VMChannel_read__ILjava_nio_ByteBuffer_2 (JNIEnv *env,
+Java_gnu_java_nio_VMChannel_read__ILjava_nio_ByteBuffer_2II (JNIEnv *env,
                                                            jobject o __attribute__ ((__unused__)), 
                                                            jint fd, 
-                                                           jobject bbuf)
+                                                           jobject bbuf,
+                                                           jint position,
+                                                           jint len)
 {
 #ifdef HAVE_READ
-  jint len;
   ssize_t result;
   struct JCL_buffer buf;
   int tmp_errno;
 
-/*   NIODBG("fd: %d; bbuf: %p", fd, bbuf); */
+   /* NIODBG("fd: %d; bbuf: %p, pos: %d, len: %d", fd, bbuf,position,len);  */
   
-  if (JCL_init_buffer(env, &buf, bbuf) < 0)
+  if (JCL_init_buffer_fast(env, &buf, bbuf,position,position+len) < 0)
     {
       /* TODO: Rethrown exception */
       JCL_ThrowException (env, IO_EXCEPTION, "Buffer initialisation failed");
       return -1;
     }
 
-  len = buf.limit - buf.position;
-
   if (len == 0)
     {
-      JCL_release_buffer (env, &buf, bbuf, JNI_ABORT);
+      JCL_release_buffer_fast (env, &buf, bbuf, JNI_ABORT);
       return 0;
     }
   
@@ -422,20 +440,20 @@
         result = 0;
       else if (errno == EBADF) /* Bad fd */
         {
-          JCL_release_buffer(env, &buf, bbuf, JNI_ABORT);
+          JCL_release_buffer_fast(env, &buf, bbuf, JNI_ABORT);
           JCL_ThrowException (env, NON_READABLE_CHANNEL_EXCEPTION, 
                               strerror(errno));
           return -1;
         }
       else if (EINTR == errno) /* read interrupted */
         {
-          JCL_release_buffer(env, &buf, bbuf, JNI_ABORT);
+          JCL_release_buffer_fast(env, &buf, bbuf, JNI_ABORT);
           JCL_ThrowException(env, INTERRUPTED_IO_EXCEPTION, strerror (errno));
           return -1;
         }
       else
         {
-          JCL_release_buffer(env, &buf, bbuf, JNI_ABORT);
+          JCL_release_buffer_fast(env, &buf, bbuf, JNI_ABORT);
       	  JCL_ThrowException (env, IO_EXCEPTION, strerror(errno));
       	  return -1;
         }
@@ -443,7 +461,7 @@
   else 
     buf.count = result;
       
-  JCL_release_buffer(env, &buf, bbuf, 0);
+  JCL_release_buffer_fast(env, &buf, bbuf, 0);
   
   return result;
 #else
@@ -454,32 +472,32 @@
 #endif /* HAVE_READ */
 }
 
+
 JNIEXPORT jint JNICALL 
-Java_gnu_java_nio_VMChannel_write__ILjava_nio_ByteBuffer_2 (JNIEnv *env, 
+Java_gnu_java_nio_VMChannel_write__ILjava_nio_ByteBuffer_2II (JNIEnv *env, 
                                                             jobject o __attribute__ ((__unused__)), 
                                                             jint fd, 
-                                                            jobject bbuf)
+                                                            jobject bbuf,
+                                                            jint position,
+                                                            jint len)
 {
 #ifdef HAVE_WRITE
-  jint len;
   ssize_t result;
   struct JCL_buffer buf;
   int tmp_errno;
 
 /*   NIODBG("fd: %d; bbuf: %p", fd, bbuf); */
   
-  if (JCL_init_buffer(env, &buf, bbuf) < 0)
+  if (JCL_init_buffer_fast(env, &buf, bbuf, position, position+len) < 0)
     {
       /* TODO: Rethrown exception */
       JCL_ThrowException (env, IO_EXCEPTION, "Buffer initialisation failed");
       return -1;
     }
 
-  len = buf.limit - buf.position;
-
   if (len == 0)
     {
-      JCL_release_buffer (env, &buf, bbuf, JNI_ABORT);
+      JCL_release_buffer_fast (env, &buf, bbuf, JNI_ABORT);
       return 0;
     }
   
@@ -501,13 +519,13 @@
         }
       else
         {
-          JCL_release_buffer(env, &buf, bbuf, JNI_ABORT);
+          JCL_release_buffer_fast(env, &buf, bbuf, JNI_ABORT);
           JCL_ThrowException(env, IO_EXCEPTION, strerror(errno));
           return -1;
         }
     }
     
-  JCL_release_buffer(env, &buf, bbuf, JNI_ABORT);
+  JCL_release_buffer_fast(env, &buf, bbuf, JNI_ABORT);
   
   return result;
 #else
diff -r -N -w -u -I '[$]Id:' --exclude='*.class' --exclude='.*' --exclude='*.orig' --exclude='*.rej' --exclude=CVS --exclude='#*' --exclude='*~' ./include/gnu_java_nio_VMChannel.h ./include/gnu_java_nio_VMChannel.h
--- ./include/gnu_java_nio_VMChannel.h	2006-12-09 06:39:27.000000000 +1100
+++ ./include/gnu_java_nio_VMChannel.h	2007-01-29 22:51:01.000000000 +1100
@@ -15,11 +15,11 @@
 JNIEXPORT jint JNICALL Java_gnu_java_nio_VMChannel_stderr_1fd (JNIEnv *env, jclass);
 JNIEXPORT void JNICALL Java_gnu_java_nio_VMChannel_setBlocking (JNIEnv *env, jclass, jint, jboolean);
 JNIEXPORT jint JNICALL Java_gnu_java_nio_VMChannel_available (JNIEnv *env, jclass, jint);
-JNIEXPORT jint JNICALL Java_gnu_java_nio_VMChannel_read__ILjava_nio_ByteBuffer_2 (JNIEnv *env, jclass, jint, jobject);
+JNIEXPORT jint JNICALL Java_gnu_java_nio_VMChannel_read__ILjava_nio_ByteBuffer_2II (JNIEnv *env, jclass, jint, jobject, jint, jint);
 JNIEXPORT jint JNICALL Java_gnu_java_nio_VMChannel_read__I (JNIEnv *env, jclass, jint);
 JNIEXPORT jlong JNICALL Java_gnu_java_nio_VMChannel_readScattering (JNIEnv *env, jclass, jint, jobjectArray, jint, jint);
 JNIEXPORT jint JNICALL Java_gnu_java_nio_VMChannel_receive (JNIEnv *env, jclass, jint, jobject, jobject);
-JNIEXPORT jint JNICALL Java_gnu_java_nio_VMChannel_write__ILjava_nio_ByteBuffer_2 (JNIEnv *env, jobject, jint, jobject);
+JNIEXPORT jint JNICALL Java_gnu_java_nio_VMChannel_write__ILjava_nio_ByteBuffer_2II (JNIEnv *env, jobject, jint, jobject, jint, jint);
 JNIEXPORT jlong JNICALL Java_gnu_java_nio_VMChannel_writeGathering (JNIEnv *env, jobject, jint, jobjectArray, jint, jint);
 JNIEXPORT jint JNICALL Java_gnu_java_nio_VMChannel_send (JNIEnv *env, jclass, jint, jobject, jbyteArray, jint);
 JNIEXPORT jint JNICALL Java_gnu_java_nio_VMChannel_send6 (JNIEnv *env, jclass, jint, jobject, jbyteArray, jint);
diff -r -N -w -u -I '[$]Id:' --exclude='*.class' --exclude='.*' --exclude='*.orig' --exclude='*.rej' --exclude=CVS --exclude='#*' --exclude='*~' ./vm/reference/gnu/java/nio/VMChannel.java ./vm/reference/gnu/java/nio/VMChannel.java
--- ./vm/reference/gnu/java/nio/VMChannel.java	2006-12-05 09:17:12.000000000 +1100
+++ ./vm/reference/gnu/java/nio/VMChannel.java	2007-02-02 18:36:20.000000000 +1100
@@ -146,6 +146,10 @@
   
   private static native int available(int native_fd) throws IOException;
 
+  public static final ByteBuffer wrap(byte[] buf, int pos, int len) {
+    return ByteBuffer.wrap(buf,pos,len);
+  }
+  
   /**
    * Reads a byte buffer directly using the supplied file descriptor.
    * 
@@ -156,10 +160,13 @@
   public int read(ByteBuffer dst)
     throws IOException
   {
-    return read(nfd.getNativeFD(), dst);
+    int bytes = read(nfd.getNativeFD(), dst, dst.position(), dst.limit()-dst.position());
+    if (bytes > 0)
+      dst.position(dst.position()+bytes);
+    return bytes;
   }
   
-  private static native int read(int fd, ByteBuffer dst) throws IOException;
+  private static native int read(int fd, ByteBuffer dst, int position, int len) throws IOException;
   
   /**
    * Read a single byte.
@@ -244,12 +251,16 @@
    * @return Number of bytes written.
    * @throws IOException
    */
-  public int write(ByteBuffer src) throws IOException
+  public int write(ByteBuffer src)
+  throws IOException
   {
-    return write(nfd.getNativeFD(), src);
+    int bytes = write(nfd.getNativeFD(), src, src.position(), src.limit()-src.position());
+    if (bytes > 0)
+      src.position(src.position()+bytes);
+    return bytes;
   }
   
-  private native int write(int fd, ByteBuffer src) throws IOException;
+  private native int write(int fd, ByteBuffer src, int pos, int len) throws IOException;
 
   /**
    * Writes from byte buffers directly using the supplied file descriptor.

Reply via email to