None of your results are too surprising to me. In JRuby, we moved several structures from encapsulating an array to using differently-sized "slotted" objects, with performance registering much better as a result. Some of that was due to the lack of boundschecks on the arrays, but another portion was probably due to the reduced size of an object + reference versus object + reference + array.
Some comments inline below. On Sun, Nov 1, 2009 at 7:48 PM, <[email protected]> wrote: > public class ArrayVsClass { > > public static void main(String[] args) { > long lVectorStartTime = System.currentTimeMillis(); > int iterations = Integer.MAX_VALUE; > while (iterations-- > 0) { > // int iterations2 = 10; > //while (iterations2-- > 0) > { > //testArray(); // ARRAY > // vs > //testGETFIELD(); // GETFIELD > // vs > testIBean(); // INVOKE INTERFACE > // vs > //testBean(); // INVOKE VIRTUAL > // vs > //testABean(); // INVOKE VIRTUAL POSSIBLY THROW > // vs > //testSlots(); // INVOKE FOR AVALUE > } > } > long lVectorRunTime = System.currentTimeMillis() - lVectorStartTime; > System.out.println("Bench time: " + lVectorRunTime); > > } Because of optimization effects, you should try running them all together in the same benchmark in varying orders. Short benchmarks like these can skew actual results because only a small subset of the full code needs to be considered for optimization. > // SLOTS time: 33157,33250,33156 > public static void testSlots() { > ClassWithSlots oneSlot = new ClassWithSlots(6); ... > // Array time: 18438,18437,18422 > public static void testArray() { > final long[] accessArray = new long[] { 6 }; Not too surprising; you're paying the cost of the array plus the cost of the virtual invocation. So even after inlining, you've got something like boundscheck + deref + virtual call. > // GETFIELD time: 14688,14531,14453 > public static void testGETFIELD() { > ClassWithOneSlot oneSlot = new ClassWithOneSlot(6); ... > // INVOKE VIRTUAL time: 14750,14594,14719 > public static void testBean() { > ClassWithOneSlot oneSlot = new ClassWithOneSlot(6); This is exactly the pattern we use in JRuby for heap-based scopes. We have from ZeroVarDynamicScope up to FourVarDynamicScope and then it falls over into an array-based version. Because we can statically tell how many variable slots we'll need in most Ruby scopes, this ended up being a big perf improvement for us. > // INVOKE INTERFACE time: 14469,14610,14859 > public static void testIBean() { > IBeanWithOneSlot oneSlot = new ClassWithOneSlot(6); > > int iterations = Integer.MAX_VALUE; > long result = 0; > while (iterations-- > 0) { > result += oneSlot.getValue(); > } > } invokeinterface ends up as fast as invokevirtual once it's been inlined, so this is no surprise. > // INVOKE VIRTUAL POSSIBLY THROW time: 14641,14594,14547 > public static void testABean() { > AClassWithOneSlot oneSlot = new ClassWithOneSlot(6); Exception-handling paths not followed do not impact performance, so this is also not surprising. Try having one out of the N invocations trigger the exception and watch the perf change drastically from then on. - Charlie --~--~---------~--~----~------------~-------~--~----~ You received this message because you are subscribed to the Google Groups "JVM Languages" group. To post to this group, send email to [email protected] To unsubscribe from this group, send email to [email protected] For more options, visit this group at http://groups.google.com/group/jvm-languages?hl=en -~----------~----~----~----~------~----~------~--~---
