----- Original Message ----- From: "Charles Oliver Nutter" headius@headius.com To: jvm-languages@googlegroups.com Sent: Sunday, November 01, 2009 8:47 PM Subject: [jvm-l] Re: Some Array vs GETFILED access times
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, logicmoo@gmail.com 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 jvm-languages@googlegroups.com To unsubscribe from this group, send email to jvm-languages+unsubscribe@googlegroups.com For more options, visit this group at http://groups.google.com/group/jvm-languages?hl=en -~----------~----~----~----~------~----~------~--~---