# Wednesday, 24 November 2004
Direct ByteBuffer, Inlining & New Snapshot

Michael Koch finished Classpath's implementation of DirectByteBufferImpl. Direct ByteBuffers allow Java code to access unmanaged memory. You can allocate them from Java using ByteBuffer.allocateDirect or from JNI using NewDirectByteBuffer. The default Classpath implementation uses native methods to implement accessing the buffer, but for IKVM I really didn't want to do it that way, because I like to use managed code whenever possible (for both maintainability and portability) and also because native method invocation is fairly slow. I therefore decided to reimplement gnu.classpath.RawData (the Classpath abstract native memory pointer) and java.nio.VMDirectByteBuffer.

At first RawData was a value type containing a single IntPtr field in an attempt to avoid an indirection when loading the pointer. This required that I replaced the implementation of DirectByteBufferImpl.get/put with some handwritten CIL instructions, because the compiled Java code resulted in unnecessarily boxing the RawData struct and that obviously killed performance. Since I was replacing the get and put methods anyway, I also inlined the index check and position update. After some experimentation I discovered that making RawData a value type didn't really help performance (the .NET JIT isn't very good at taking advantage of value type optimization opportunities) so I changed it into a reference type, this removed the need for special casing get and put, but when I removed the handcoded CIL performance went down significantly (approx. 50% slower). For some reason the JIT wasn't inlining the index check method, so I left the handcoded CIL in map.xml.

Here's a random microbenchmark:

import java.nio.*;
class DirectByteBuffer {
  public static void main(String[] args) {
    final int SIZE = 10 * 1024 * 1024;
    for(int j = 0; j < 2; j++) {
      ByteBuffer b = ByteBuffer.allocateDirect(SIZE);
      long start = System.currentTimeMillis();
      for(int i = 0; i < SIZE; i++)
        b.put(i, (byte)i);
      long sum = 0;
      for(int i = 0; i < SIZE; i++)
        sum += b.get(i) & 0xFF;
      long end = System.currentTimeMillis();
      System.out.println(end - start);
      System.out.println("sum = " + sum);
    }
  }
}

Results:

JRE 1.4.2 HotSpot Client VM Average
671 530 541 521 461 545
450 461 450 460 460 456
           
JRE 1.5 HotSpot Client VM  
401 461 411 481 480 447
410 360 441 410 411 406
           
JRE 1.5 HotSpot Server VM  
220 120 110 120 120 138
100 110 110 160 100 116
           
IKVM on .NET 1.1  
201 231 231 240 251 231
210 210 210 221 220 214
           
IKVM on Mono 1.0.2 (--optimize=all)  
430 391 400 401 390 402
401 390 401 390 401 397


Pretty good results. Most other operations perform similarly, except for compact, copy a large buffer takes about twice as long as on the JRE. The .NET class library really should have a memcpy or memmove method that is properly optimized. Note that there is CIL instruction cpblk to copy memory and I tried that as well, it is about 30% faster than my straightforward C# loop, but it doesn't handle overlapping regions and I didn't want to use an unverfiable instruction anyway (as that would cause IKVM.GNU.Classpath to fail verification and I use verification as simple smoke test during build to catch the most obvious regressions in the compiler).

I've also updated the Japi status results: IKVM vs JDK and JDK vs IKVM.

Other changes:

  • Resync'ed with GNU Classpath cvs.
  • Removed java/net/SocketInputStream.java and java/net/SocketOutputStream.java.
  • Moved contents of java/net/PlainSocketImpl.java to gnu/java/net/PlainSocketImpl.java.
  • Moved contents of java/net/PlainDatagramSocketImpl.java to gnu/java/net/PlainDatagramSocketImpl.java.
  • Added deprecated attribute to the three deprecated String methods.
  • Added deprecated attribute support to map.xml parser.
  • Added deprecated attribute support to ClassFileWriter.
  • Added deprecated attribute support to ikvmstub.
  • Added public API to query deprecatedness of fields, methods, constructors and classes.
  • Changed ikvmstub to emit serialVersionUID fields even if it is private, to make Japicompat status more accurate.
  • Added public API to query the constant value of a field.
  • Added ConstantValueAttribute to persist the constant value of constant instance fields (which are treated the same as static constant fields, by javac).
  • Added support to ikvmstub for constant instance fields.
  • Changed japi build file to generate both forward and backward compatibility reports.
  • Fixed runtime to support loading a pre-compiled Java class by referencing an ikvmstub generated stub class.
  • Fixed module names when generating -Xsave files.
  • Moved JNI method pointer field to proxy class when generating -Xsave files.
  • Replaced gnu.classpath.RawData with an IKVM specific version.
  • Added optimized special case implementation of several java.nio.DirectByteBufferImpl methods to map.xml.
  • Removed CharConversionException handlers from StringHelper (I fixed Classpath's character/string encoders/decoders to no longer throw exceptions).
  • ClassLoaderWrapper has a new method PublishLibraryImplementationHelperType to publish types that live in IKVM.Runtime.dll (this is used for the new gnu.classpath.RawData that lives in IKVM.Runtime.dll because it contains unverifiable code).
  • Improved line number table encoding (more space efficient and simpler code).
  • Implemented JNI methods NewDirectByteBuffer, GetDirectBufferAddress and GetDirectBufferCapacity.
  • Added new method AttributeHelper.SetEditorBrowsableNever to decorate methods with the EditorBrowsableAttribute (to prevent them from showing up in Intellisense).
  • Implemented support to override method implementations in map.xml (not implemented for constructors yet).
  • Renamed all of the IKVM introduced fields and methods to __<xxx>.
  • Added support for several new CIL instructions to remapper.cs.
  • Shadow types (e.g. the .NET java.lang.Object type) now hide all inherited methods and decorate them with EditorBrowsableAttribute(Never) so that they no longer show up in Intellisense.
  • I broke JNI.RegisterNatives in this snapshot (but it's fixed in cvs).

New snapshots: just the binaries and source plus binaries.

Wednesday, 24 November 2004 13:56:56 (W. Europe Standard Time, UTC+01:00)  #    Comments [0]
# Tuesday, 09 November 2004
New Snapshot

A new snapshot. Nothing new, except that FileDescriptor.sync now actually works on Linux.

Thanks to lupus for reporting that it didn't work before.

New snapshots: just the binaries and source plus binaries.

Tuesday, 09 November 2004 17:37:29 (W. Europe Standard Time, UTC+01:00)  #    Comments [0]
Comment Spam

Yesterday I disabled comments, because the comment spam was driving me nuts. I've now re-enabled them, but I've added a programming question that hopefully should be easy to answer for anyone wishing to comment, but too hard for the spammers to be worthwhile to figure out.

If you want to comment but don't know the answer to the question, feel free to e-mail me.

Sorry, for the inconvenience. :-(

Tuesday, 09 November 2004 15:02:36 (W. Europe Standard Time, UTC+01:00)  #    Comments [2]
# Monday, 08 November 2004
Running Derby on IKVM

At the Colorado Software Summit I attended a presentation on Derby by Dan Debrunner. Derby is the open source version of IBM's Cloudscape database engine. After seeing the presentation I decided to try to run it on IKVM. I tried some simple tests and they all went fine. At the moment Derby doesn't include a test suite yet (IBM is working on releasing their tests), so I didn't do any extensive testing.

Since Derby is a real transactional database engine it uses a transaction log and requires FileDescriptor.sync() to work corectly, so I decided to implement that. I hadn't previously implemented it, because it unfortunately requires platform specific code, so it only works on Windows and on Posix systems (when running on Mono).

I wrote a very simple test app in C# (and the Java equivalent) and did some performance tests (download source):

Operation Sun JDK 1.5 IKVM/.NET 1.1 IKVM/Mono 1.0.2
getConnection 1903 1472 2177
create table 450 634 624
prepareStatement1 344 514 978
1000x insert 1031 387 550
prepareStatement2 124 294 231
1000x select 1038 851 3682

(Times in milliseconds, based on average of three best runs out of four runs.)

All the usual disclaimers about benchmarks apply of course, but it's interesting to see that IKVM on .NET outperforms JDK 1.5 on the queries. Preparing the statements take longs, but that is expected, as IKVM has to convert the generated class files to CIL (and this part of IKVM is not very efficient).

Another interesting aside is that when I ran the test on JDK 1.4.1, the insert operation took about 40 seconds to run. This rather extreme performance bug was fixed in 1.4.2.

One thing that is important to note when running Derby on IKVM is that Derby compiles all queries to bytecode and IKVM doesn't support garbage collecting classes (due to .NET's inability to unload code), so unless your application uses a fixed set of prepared statements that fit in the Derby statement cache, you're going to leak memory.

I made a new snapshot that includes the new sync implementation:

  • Resync'ed with GNU Classpath cvs.
  • Implemented java.io.FileDescriptor.sync() and java.nio.channels.FileChannel.force().
  • Fixed java.io.File to treat file paths as case insensitive on Windows.
  • Made exception helper a little more robust against exceptions happening early in bootstrap.
  • Fixed japi build file to include all the public APIs.
  • Fixed ClassLoaderWrapper.getSystemClassLoader() to bypass ClassLoader.getSystemClassLoader() and access the underlying field directly, to prevent a security check.
  • Changed DotNetTypeWrapper to overrider GetClassLoader to lazily get the system class loader.
  • Fixed ikvmc generated main stub to return 1 if the program exited with an exception.
  • Implemented support for java.security.manager system property (the security manager can now be set from the command line using -Djava.security.manager=<class>).

New snapshots: just the binaries and source plus binaries.

Monday, 08 November 2004 11:51:13 (W. Europe Standard Time, UTC+01:00)  #    Comments [8]
# Thursday, 04 November 2004
Japitools & New Snapshot

I've integrated Stuart Ballard's Japitools in my build process to keep track of the API completion status and to test ikvmc and ikvmstub. To results of running it on the current ikvm development snapshot can be found here.

What's new in the snapshot:

  • Implemented AWT Toolkit.getFontList().
  • Regenerated mscorlib, System and System.Xml stub jars with new version of ikvmstub.
  • Resync'ed with GNU Classpath cvs.
  • Improved error handling in Socket classes (exceptions throw now match the JDK better).
  • Implemented urgent data and shutdown methods in PlainSocketImpl (thanks to Dmitry Gromov for this).
  • Fixed ikvm.exe and ikvmc.exe to support slashes in Main-Class entry in manifest.
  • Fixed ikvmstub.exe to emit throws clauses for constructors.
  • Fixed bug that caused internal error when compiling class that has a non-private final field of a type that cannot be loaded.
  • Fixed reflection on .NET types to mark non-virtual methods as final.
  • Fixed name clash detection algorithm to ignore constructors (because name clash prevention isn't yet implemented for constructors).
  • Fixed reflection on .NET types to ignore newslot methods that hide a virtual method in a base class.
  • Fixed reflection on .NET types to not emit a public for explicitly implemented interface methods if a base class already has a suitable method.
  • Removed HideFromJavaAttribute from remapped types.
  • Added HideFromJavaAttribute to nested helper type generated for remapped interfaces.

New snapshots: just the binaries and source plus binaries.

Thursday, 04 November 2004 14:13:12 (W. Europe Standard Time, UTC+01:00)  #    Comments [3]