Andy Malakov software blog

Tuesday, August 5, 2008

Update on "direct vs. encapsulated field access times"

Update to earlier post about performance of direct field versus method-encapsulated access. A friend of mine pointed to strange decrease in method access performance right before it is getting compiled to native code. So I went back and I enhanced output of the benchmark:


static void benchmark () throws Exception {
FieldAccessor fv = new FieldAccessor ();
MethodAssessor mv = new MethodAssessor ();

System.err.println ("\tTotal, Method, Field, Ratio");
for (int n=1, total = 0; n < 10000000; n=2*n) {
final long t0 = System.nanoTime();
for (int i = 0; i < n; i++) {
if ((mv.getField() & 0x101) != 0)
mv.setField(mv.getField()+1);
else
mv.setField(mv.getField()+2);
}
final long t1 = System.nanoTime();
for (int i = 0; i < n; i++) {
if ((fv.field & 0x101) != 0)
fv.field = (fv.field+1);
else
fv.field = (fv.field+2);
}
final long t2 = System.nanoTime();

// dispay stats
final double mvCycleTime = (double)(t1 - t0) / n;
final double fvCycleTime = (double)(t2 - t1) / n;
final double ratio = 100.0*fvCycleTime/mvCycleTime;
total +=n;
System.err.printf("% 12d, %.3f, %.3f, %.2f%% %c\n",
total, mvCycleTime, fvCycleTime, ratio,
(fv.field == mv.getField() ? ' ' : '!'));

Thread.sleep(500);
}
}

This code produces the following output (with -XX:+PrintCompilation):

Total, Method, Field, Ratio
1, 5866.000, 1759.000, 29.99%
3, 5463.000, 405.500, 7.42%
7, 1925.500, 217.750, 11.31%
15, 1166.000, 156.250, 13.40%
31, 648.313, 123.938, 19.12%
63, 440.438, 110.250, 25.03%
127, 401.922, 103.547, 25.76%
255, 270.688, 106.156, 39.22%
511, 243.715, 95.785, 39.30%
1023, 225.525, 94.367, 41.84%
2047, 231.510, 142.017, 61.34%
4095, 232.009, 97.745, 42.13%
2 tmp.TestFieldVsMethod$MethodAssessor::getField (5 bytes)
8191, 227.914, 97.593, 42.82%
3 tmp.TestFieldVsMethod$MethodAssessor::setField (6 bytes)
16383, 238.234, 98.486, 41.34%
1% tmp.TestFieldVsMethod::benchmark @ 44 (277 bytes)
32767, 13.628, 1.675, 12.29%
65535, 1.621, 1.666, 102.78%
131071, 1.611, 1.658, 102.88%
262143, 1.602, 1.655, 103.31%
524287, 1.599, 1.653, 103.41%
1048575, 1.897, 1.923, 101.40%
4 java.lang.String::charAt (33 bytes)
2097151, 1.606, 1.652, 102.88%
4194303, 1.595, 1.652, 103.52%
8388607, 1.595, 1.652, 103.53%
16777215, 1.595, 1.655, 103.76%


Notice how ratio dives down at around 30,000 mark:



What that probably means is that field access code is compiled faster:



Without knowing much about hotspot inner workings, I expected to see a sharper decline.
I run this test on server version of Java 6 (1.6.0_10-beta-b25, 64 bit).