As part of my job, I have been writing some Java code to do some pretty complex data moving and processing, and was getting great performance results on my laptop. The next step was to test the performance on a "Production" system - usually thought of as Unix/Linux. The first test was on Solaris. To my disappointment, the numbers weren't that great.
So I created a little benchmark class (others must have also done this before, but nothing was handy) to rule out any of the processing and test the performance of basic functions.
This class does 5 things:
1) A simple for loop with a billion iterations
2) A loop doing simple FP processing (d = 34.0, for(..) d= (d* 23.7)/56.9
3) Writing a 50MB filling it with ASCII '0123456789' repeated
4) Reading that file using fis.read()
5) Reading the file using a 1MB memory map block read
The results I got kind of shocked me, so I was wondering if anyone could shed any light on them. Both of these results are using exactly the same code, JSDK1.4.2, and the same VM params (Client [HotSpot] mode).
Windows XP on my Laptop: Dell Latitude 600, 1.6GHz, 1GB Ram
Starting Benchmark
Counting to 1,000,000,000...took 5060ms
Doing 500,000,000 FP ops...took 6320ms
Writing a 50MB file...took 7043ms
Reading the 50MB file using FIS.read...took 1893ms
Reading the 50MB file using ByteBuffer...took 531ms
Sun-Fire-V440 Solaris 5.10, 2x1GHz, 4GB Ram
Starting Benchmark
Counting to 1,000,000,000...took 16753ms
Doing 500,000,000 FP ops...took 14140ms
Writing a 50MB file...took 2031ms
Reading the 50MB file using FIS.read...took 7058ms
Reading the 50MB file using ByteBuffer...took 1430ms
So the basic iterations were 3 1/2 times slower, writing a file 3 1/2 times faster, but reading 3 1/2 times slower on Solaris than Windows.
If anyone could repeat this test and past the results as comments here, I'd really appreciate it. I would like to know if this is expected behavior for Solaris, or if our machine needs its kernel tuned. I have also tried it on our AIX, Linux and HP-UX machines, using Xeon and Itanium processors and gotten equally disappointing results.
This article is copied in my blog together with a link to download the Java File. I'm going to do some more research, but would love to find out how other peoples systems perform.
-
Is Java Slower on Solaris than Windows? (4 messages)
- Posted by: Be Excellent
- Posted on: August 04 2005 18:37 EDT
Threaded Messages (4)
- it's not the O/S... by Alex Chang on August 06 2005 10:13 EDT
- Updated BenchMark for -server setting by Be Excellent on August 08 2005 16:00 EDT
- scanning bytebuffers by Jason Frank on August 14 2005 22:24 EDT
- scanning bytebuffers by Be Excellent on August 15 2005 13:21 EDT
-
it's not the O/S...[ Go to top ]
- Posted by: Alex Chang
- Posted on: August 06 2005 10:13 EDT
- in response to Be Excellent
i'm spearheading a transition of my company's commercial java products onto the Opteron platform. The Sparc cpu's are just slow. some of my benchmarks show that applications on a system with the 2.2Ghz Opteron is ~3 times faster than the 1.5Ghz Sparc IIIi, with same memory. Of course, when it comes to disks, you can always optimize with RAID (etc) to suit your purposes. Solaris rocks though (porting from Sparc to x64 was a breeze). -
Updated BenchMark for -server setting[ Go to top ]
- Posted by: Be Excellent
- Posted on: August 08 2005 16:00 EDT
- in response to Alex Chang
I've updated the BenchMark to have usual basic operations and FP testing that are not optimized out by the -server flag. I also added a large array and CRC64 test.
What I really want to do is improve the file reading performance and basic operations for Java on Solaris (Sparc). Any pointers would be greatly appreciated. -
scanning bytebuffers[ Go to top ]
- Posted by: Jason Frank
- Posted on: August 14 2005 22:24 EDT
- in response to Be Excellent
I modified your code to do some real work, in this case scanning the file for the ascii code for "5", which is 53.
{
_resetBuffer();
System.out.print("Reading the 50MB file using ByteBuffer...");
start = (new Date()).getTime();
FileInputStream fis = null;
int k = 0;
try {
fis = new FileInputStream("/tmp/test.txt");
byte[] buffer = new byte[50];
while (_readUsingBuffer(fis, buffer) != -1) {
for (int i = 0; i < buffer.length; i++) {
if (buffer[i] == 53) {
k++;
}
}
}
} catch (Exception e) {
System.err.println("Exception: " + e);
e.printStackTrace();
} finally {
try {
fis.close();
} catch (Exception ee) {
}
}
end = (new Date()).getTime();
System.out.println("took " + (end - start) + "ms, found " + k
+ " fives.");
}
{
_resetBuffer();
System.out.print("Reading the 50MB file using ByteBuffer...");
start = (new Date()).getTime();
FileInputStream fis = null;
int k = 0;
try {
fis = new FileInputStream("/tmp/test.txt");
fChannel = fis.getChannel();
long pos = 0;
long bSize = 5 * 1024 * 1024;
long fcSize = fChannel.size();
while (pos < fChannel.size()) {
if (pos + bSize > fcSize) {
bSize = fcSize - pos;
}
MappedByteBuffer mBuf = fChannel.map(
FileChannel.MapMode.READ_ONLY, pos, bSize);
ByteBuffer bBuf = mBuf.asReadOnlyBuffer();
while (bBuf.hasRemaining()) {
if (bBuf.get() == 53) {
k++;
}
}
pos += bSize;
}
} catch (Exception e) {
System.err.println("Exception: " + e);
e.printStackTrace();
} finally {
try {
fis.close();
} catch (Exception ee) {
}
}
end = (new Date()).getTime();
System.out.println("took " + (end - start) + "ms, found " + k
+ " fives.");
}
Then I wrote another scan method using idioms straight out of the nio examples from java.sun.com, and the results were quite different:
1062ms scanning with byte array copies of length 50.
590ms scanning using a mapped byte buffer and one byte at a time.
Almost half the time. This was done on Linux 2.6.8, 1.4.2 JVM, on a P4 3.0E (prescott). I think you might find that solaris would be happier if you scanned the bytes or did whatever work you needed to do against the byte buffer directly, as opposed to slowly copying it back into primitive bytes. -
scanning bytebuffers[ Go to top ]
- Posted by: Be Excellent
- Posted on: August 15 2005 13:21 EDT
- in response to Jason Frank
Thanks for the suggestion. I guess what is missing in my posts is the context of the problem. The benchmark was constructed to test certain aspect of the actual app I am writing. Reading a byte array of length n from a file is part of the app, reading individual bytes is not. So, although this may be faster, it doesn't really help in my case.
I also tested your code on my laptop, and interestingly get exactly the opposite results, with the almost the same speed. My laptop is only a 1.6GHz and has a laptop hard-drive, which has to be slower than your linux machine (doesn't it):
Finding fives scanning byte array copies of length 50...took 640ms, found 5242060 fives.
Finding fives using mapped byte buffer a byte at a time...took 1051ms, found 5242061 fives.
On the Solaris box:
Finding fives scanning byte array copies of length 50...took 1415ms, found 5242060 fives.
Finding fives using mapped byte buffer a byte at a time...took 1659ms, found 5242061 fives.
Must be something about the Linux JVM or filesystem. With the fastest method here I'm still getting Solaris file reading less than half as fast as my laptop.