Andy Malakov software blog
Thursday, August 14, 2008
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:
This code produces the following output (with -XX:+PrintCompilation):
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).
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).
Java NIO with large data files
We have a tool that sequentially reads financial data from massive multi-gigabyte files. It performs a lot of bytes-to-long, bytes-to-double, and utf-string conversions that were implemented in optimized fashion on top of BufferedInputStream.
It turns out that replacing that code with NIO's MappedByteBuffer processes 2 Gb file twice faster, while memory consumption remains the same.
Update: I have been warned and it turned out to be true: Reading 20 Gb file shows reverse results. Now NIO version is 8x slower than "old" stream-based approach. In this case I implemented paging because Java NIO cannot map file regions larger than 2Gb at once (Integer.MAX_VALUE).
On Windows XP 8K is a sweet spot for BufferedInputStream buffer size. Allocating larger buffer actually reduces performance significantly. May be size of mapped region has similar optimum.
It turns out that replacing that code with NIO's MappedByteBuffer processes 2 Gb file twice faster, while memory consumption remains the same.
Update: I have been warned and it turned out to be true: Reading 20 Gb file shows reverse results. Now NIO version is 8x slower than "old" stream-based approach. In this case I implemented paging because Java NIO cannot map file regions larger than 2Gb at once (Integer.MAX_VALUE).
On Windows XP 8K is a sweet spot for BufferedInputStream buffer size. Allocating larger buffer actually reduces performance significantly. May be size of mapped region has similar optimum.
Direct field vs. Method access times
In my opinion there is no justification for using 'friendly' direct access to mutable class fields rather than getters and setters properties. I heard the following two arguments:
False. There is no benefit of direct access in frequently executed code. Chart below compares access time of direct field access to getters and setters.
As you can see, after 20,000 calls there is no difference in performance. This threshold is controlled by -XX:CompileThreshold JVM argument. Core of the test:
Read: I am too lazy to write getters and setters. Well, Eclipse and Netbeans have "Encapsulate Field" and "Generate getters and Setters" commands. And BTW, what none of free IDEs have is "Find where modified" search for class fields.
Having said that, I have no problems with public final fields :-)
1. It makes performance-critical code run faster.
False. There is no benefit of direct access in frequently executed code. Chart below compares access time of direct field access to getters and setters.
As you can see, after 20,000 calls there is no difference in performance. This threshold is controlled by -XX:CompileThreshold JVM argument. Core of the test:
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();
2. Direct field access reduces bureaucracy.
Read: I am too lazy to write getters and setters. Well, Eclipse and Netbeans have "Encapsulate Field" and "Generate getters and Setters" commands. And BTW, what none of free IDEs have is "Find where modified" search for class fields.
Having said that, I have no problems with public final fields :-)
Subscribe to:
Posts (Atom)
Blog Archive
- November 2024 (1)
- November 2016 (1)
- March 2015 (2)
- December 2011 (1)
- October 2010 (2)
- June 2010 (1)
- May 2010 (1)
- December 2009 (1)
- November 2009 (4)
- October 2009 (1)
- July 2009 (2)
- June 2009 (1)
- December 2008 (1)
- September 2008 (2)
- August 2008 (4)
- July 2008 (3)
- June 2008 (4)
- May 2008 (1)