|
Quick Lists
|
|
Bug ID:
|
4137835
|
|
Votes
|
0
|
|
Synopsis
|
java.io.RandomAccessFile.writeInt(),readInt() methods results in four kernel cal
|
|
Category
|
java:classes_io
|
|
Reported Against
|
1.5
, 1.1.5
, 1.1.6
, 1.2fcs
|
|
Release Fixed
|
|
|
State
|
11-Closed,
Will Not Fix,
request for enhancement
|
|
Priority:
|
3-Medium
|
|
Related Bugs
|
4064240
,
4074338
,
4170047
,
4193259
,
4991944
,
4156970
|
|
Submit Date
|
13-MAY-1998
|
|
Description
|
Problem
-------
Synopsis: java.io.*.writeInt() methods results in four Solaris write() calls.
Summary:
The Java.io.*.writeInt() methods results in four Solaris write() system
call invocations for a single 32 bit value. The equivalent optimzed
version of the code using the Java write(byte[], int, int) method can
show significant improvements in the amount of time required to run and
a related reduction in the number of write(2) Solaris system calls made.
Code Example
------------
The following Java code reveals the underlying performance of the Solaris
implementation of java.io.*.writeInt():
import java.io.RandomAccessFile;
import java.io.IOException;
class IntWrite
{
public static void main(String args[])
throws IOException
{
RandomAccessFile raf = new RandomAccessFile("testfile.dat", "rw");
int lim = Integer.parseInt(args[0]);
for (int i=0; i<lim; i++) {
raf.writeInt(i);
}
raf.close();
}
}
-------
Contrasting this with:
import java.io.RandomAccessFile;
import java.io.IOException;
import java.lang.Integer;
class ByteArrayWrite
{
public static void main(String args[])
throws IOException
{
RandomAccessFile raf = new RandomAccessFile("testfile.dat", "rw");
byte [] a;
int lim = Integer.parseInt(args[0]);
for (int i = 0; i < lim; i++) {
a = IntToByteArray.getByteA(i);
raf.write(a,0,4);
}
raf.close();
}
}
----- Code follows for: IntToByteArray.getByteA():
//
// Take an Int, convert it to a byte[].
public class IntToByteArray {
public static void main ( String args[] ) {
byte[] b;
b = IntToByteArray.getByteA(10);
b = IntToByteArray.getByteA(-1);
}
public static byte[] getByteA(int startInt) {
//int startInt = i;
//int endInt = 0;
byte[] a = new byte[4];
// Make a byte array
a[3] = (byte) startInt;
a[2] = (byte) ( startInt >>> 8 );
a[1] = (byte) ( startInt >>> 16 );
a[0] = (byte) ( startInt >>> 24 );
//System.out.println ( " Bytes " + a[0] + " " + a[1] + " " + a
[2] + " " + a[3] );
//endInt = ((a[0]&0xff) << 24 | (a[1]&0xff) << 16 | (a[2]&0xff)
<< 8 | a[3]);
//System.out.println ( "Ending: " + endInt );
return a;
}
}
To Reproduce
------------
In my testing I took:
Solaris 2.6 3/98 + patches (see attachment "showrev-p")
Ultra 30 UltraSPARC-II 296 MHz
128Mb memory
Disk Vendor: SEAGATE
Product: ST34371W SUN4.2G
Revision: 7462
With the Solaris Java JDK 1.1.5 installed, specifically:
java full version "Solaris_JDK_1.1.5_02"
although other versions of Java can be seen to exhibit the behaviour
detailed in this bug report.
The relative performance of each can be summarized by the timed statistics
generated by the following test harnesses, firstly for the test "IntWrite":
#!/bin/ksh -x
#rm -f testfile.dat
#truss -o output.tr -t,write java IntWrite $1
#grep write output.tr| wc -l > writes
rm -f testfile.dat
/bin/time -p java IntWrite $1 >time.out.$1 2>&1
and via, the ByteArrayWrite harness:
#!/bin/ksh -x
#rm -f testfile.dat
#truss -o output.tr -t,write java ByteArrayWrite $1
#grep write output.tr| wc -l > writes
rm -f testfile.dat
/bin/time -p java ByteArrayWrite $1 >time.out.$1 2>&1
When driven over a range of values, governing the number of integers
to be written, the timings can be summarized as follows:
Times for JDK 1.1.5
kuta% grep real byteW/time.out.*
byteW/time.out.10:real 0.19
byteW/time.out.100:real 0.20
byteW/time.out.1000:real 0.22
byteW/time.out.10000:real 0.43
byteW/time.out.100000:real 2.82
byteW/time.out.1000000:real 27.48
kuta% grep real intW/time.out.*
intW/time.out.10:real 0.20
intW/time.out.100:real 0.21
intW/time.out.1000:real 0.28
intW/time.out.10000:real 0.96
intW/time.out.100000:real 8.78
intW/time.out.1000000:real 89.26
Times for JDK 1.1.6
kuta% grep real byteW/time.out.*
byteW/time.out.10:real 0.37
byteW/time.out.100:real 0.33
byteW/time.out.1000:real 0.36
byteW/time.out.10000:real 0.54
byteW/time.out.100000:real 2.24
byteW/time.out.1000000:real 18.65
kuta% grep real intW/time.out.*
intW/time.out.10:real 0.37
intW/time.out.100:real 0.34
intW/time.out.1000:real 0.39
intW/time.out.10000:real 0.88
intW/time.out.100000:real 5.45
intW/time.out.1000000:real 52.07
From the above we can highlight the performance of JDK 1.1.6:
intW/time.out.1000000:real 52.07
as the issue at the core of this bug, contrast this with the time taken to
write the equivalent interation recorded when the ByteArrayWrite class is
used:
byteW/time.out.1000000:real 18.65
Looking more deeply at what the:
IntWrite.RandomAccessFile.raf.writeInt(i);
becomes in terms of a system call to Solaris we can see via truss
that exactly four write(2) system calls are made for each
RandomAccessFile.writeInt() method invocation. Looking at a typical
truss record we see:
kuta% cat output.tr
write(8, "\0", 1) = 1
write(8, "\0", 1) = 1
write(8, "\0", 1) = 1
write(8, "\0", 1) = 1
For a call to:
truss -o output.tr -t,write java IntWrite 1
When optimized via the ByteArrayWrite class and:
ByteArrayWrite.RandomAccessFile.raf.write(a,0,4);
we can imediately cut the number of calls to the Solaris system call,
giving an improvment in run time, illustrated in the timings shown
above.
|
|
Work Around
|
N/A
|
|
Evaluation
|
This is an important problem that we should consider addressing for 1.2fcs.
While it affects DataOutputStream as well as RandomAccessFile, the latter case
is much more serious since it's impossible to use a BufferedOutputStream behind
a RandomAccessFile. It also affects the read{Int,Long,Float,Double} methods as
well as the corresponding write methods. We could either adopt the solution
given in the description or convert these methods into native methods; which is
best will have to be determined by experiment. -- xxxxx@xxxxx 5/14/1998
We converted the methods involved into native methods. This yielded some good
speedups, but unfortunately broke compatibility with existing subclasses of
RandomAccessFile (see 4170047). The optimizations have therefore been backed
out. Creating temporary byte arrays is an interesting idea but it can lead to
vastly increased object-allocation rates that would serve no purpose in
subclasses of RandomAccessFile that do their own buffering. I'm therefore
closing this bug as "will not fix." -- xxxxx@xxxxx 8/31/1998
|
|
Comments
|
PLEASE NOTE: JDK6 is formerly known as Project Mustang
|
|
|
 |