JDK-4780781 : File I/O performance does not scale with processor count (win)
  • Type: Bug
  • Component: core-libs
  • Sub-Component: java.io
  • Affected Version: 1.4.1
  • Priority: P4
  • Status: Closed
  • Resolution: Duplicate
  • OS: windows_2000
  • CPU: x86
  • Submitted: 2002-11-19
  • Updated: 2013-08-05
  • Resolved: 2013-08-05
Related Reports
Duplicate :  
Description
cNIOThread extends Thread {

		File inFile;

		public BasicNIOThread(File inFile) {
			this.inFile = inFile;
		}
		
		public void run() {
		
			FileInputStream fis = null;
		
			try {
		
				fis = new FileInputStream(inFile);
				FileChannel fic = fis.getChannel();
				ByteBuffer buffer = ByteBuffer.allocateDirect
(4096);
				
				int counter = 0;
				
				while (true) {
					int ret = fic.read(buffer);
					
					if (ret == -1) break;
					counter += ret;
					buffer.clear();
				}
			
				System.out.println("Read " + counter);
		
			
			} catch (Exception e) {
				e.printStackTrace();
			}	finally {
				//notifyAll();
				try {
					fis.close();
				} catch (Exception e2) {
					e2.printStackTrace();
				}
			}
			
		}
		
	}


	public class BasicRWNIOThread extends Thread {

		File inFile, outFile;

		public BasicRWNIOThread(File inFile, File outFile) {
			this.inFile = inFile;
			this.outFile = outFile;
		}

		public void run() {
		
			FileInputStream fis = null;
			FileOutputStream fos = null;
		
			try {
		
				fis = new FileInputStream(inFile);
				fos = new FileOutputStream(outFile);
				
				FileChannel fic = fis.getChannel();
				FileChannel foc = fos.getChannel();

				ByteBuffer buffer = ByteBuffer.allocateDirect
(1024);
				
				int counter = 0;
				
				while (true) {
					int ret = fic.read(buffer);
					if (ret == -1) break;
					counter += ret;
					buffer.flip();
					foc.write(buffer);
					buffer.clear();
				}


				System.out.println("\tRead and Wrote " +
counter + " bytes");

			} catch (Exception e) {
				e.printStackTrace();
			}	finally {
				//notifyAll();
				try {
					fis.close();
				} catch (Exception e2) {
					e2.printStackTrace();
				}
			}
			
		}
		
	}
	
	

	public class BasicRWIOThread extends Thread {
		
		
		File inFile, outFile;
		byte[] buffer = new byte[1000];
		int endPos = 0;
		
		public BasicRWIOThread(File inFile, String outfileName) {
			this.inFile = inFile;
			outFile = new File(outfileName);
		}
		
		public void run() {
			
			BufferedInputStream bis = null;
			BufferedOutputStream bos = null;
			
			endPos = 0;
			
			try {
			
				bis = new BufferedInputStream(new
FileInputStream(inFile));
				bos = new BufferedOutputStream(new
FileOutputStream(outFile));
				
				int counter = 0;
				
				while ((endPos = bis.read(buffer)) != -1) {
					counter += endPos;
					
					if (endPos > 0) {
						bos.write(buffer,0,endPos);
						endPos = 0;
					}
				}
				
				System.out.println("\t\tRead and Wrote " +
counter + " from file " +
				    inFile.getAbsolutePath() + " to file " +
outFile.getAbsolutePath());


				
			} catch (Exception e) {
				e.printStackTrace();
			}	finally {
				//notifyAll();
				try {
					bis.close();
					bos.close();
				} catch (Exception e2) {
					e2.printStackTrace();
				}
			}
		}
		
	}


	public class BasicIOThread extends Thread {
		
		File inFile;
		byte[] buffer = new byte[1000];
		int endPos = 0;
		
		public BasicIOThread(File inFile) {
			this.inFile = inFile;

		}
		
		public void run() {
			
			BufferedInputStream bis = null;
			
			endPos = 0;
			
			try {
			
				bis = new BufferedInputStream(new
FileInputStream(inFile));
				
				int counter = 0;
				
				while ((endPos = bis.read(buffer)) != -1) {
					counter += endPos;
					
				}
				
				System.out.println("\t\tRead " + counter);


				
			} catch (Exception e) {
				e.printStackTrace();
			}	finally {
				//notifyAll();
				try {
					bis.close();
				} catch (Exception e2) {
					e2.printStackTrace();
				}
			}
		}
	}



	//
===================================================================== MAIN ====
	public static void main(String[] args) {
		
		System.out.println("Running with value = '" + args[0] + "'");
		
		long value = Integer.parseInt(args[0]);
		new PureJavaDiscoverThreadBottleneck(value);
	}
}

---------- END SOURCE ----------

CUSTOMER WORKAROUND :
We must run a separate JVM to process our files on the
server.  This is unacceptable in the long run because we
must configure these separete JMVs manually for each client.
(Review ID: 166388) 
======================================================================
Name: nt126004			Date: 11/18/2002


FULL PRODUCT VERSION :
java version "1.4.1"
Java(TM) 2 Runtime Environment, Standard Edition (build 1.4.1-b21)
Java HotSpot(TM) Client VM (build 1.4.1-b21, mixed mode)


FULL OPERATING SYSTEM VERSION :
Microsoft Windows 2000 [Version 5.00.2195]

EXTRA RELEVANT SYSTEM CONFIGURATION :
Run on a 4 x 1.8G processor using a 7200prm 60 gig HD

A DESCRIPTION OF THE PROBLEM :
The basic problem is that IO access cannot scale across
threads.  However, it does scale across JVMs.  For
instance, if you have 4 threads on a 4 processor box
attempt to read and write files synchroniously within a
single JVM there is almost no scale-up.  However, if you
read and write the same files using separate JVMs (wrapped
in a single thread to maintain similar overhead just for
testing), we find there is very good scaling (approx 2:1
for 4 processors using a single, non-striped HD).

The problem is that separate JVMs create their own overhead
and are difficult to manage for a single process when
running a pure server app (non-J2EE and no pre-registration
of components on a JNDI, etc.).

STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
I'm including the code I've run.  The test requires 4 equal
files of some significant size.  My tests used files of
approx. 100 meg.  The file names can be changed to match
the constants or vice versa (all filenames are hard coded,
my apologies).  The test will create a "copy" of the file
so there must be enough room in the workspace for double
the initial file sizes.

  To run the test, the main takes in a bit position value
that determines which combination of tests to run.  The
four tests that are included are read-only, read-write,
read using NIO and read-write using NIO.  If you have any
questions, please don't hesitate to contact me.

EXPECTED VERSUS ACTUAL BEHAVIOR :
I expected the threading to have very similar scaling as
the separate JVMs.  Afterall, its the hard disk that should
be creating the bottleneck.

Unfortunately, this is not the case.  Reading from separate
(unmanageable) JVMs yields much better scaling than
threading within a JVM.

Note that C# scales up fine.  I'm trying to fight to keep
Java but this may be the "thread" that breaks the camel's
back.

ERROR MESSAGES/STACK TRACES THAT OCCUR :
No errors, just performance.

REPRODUCIBILITY :
This bug can be reproduced always.

---------- BEGIN SOURCE ----------
import java.io.*;
import java.nio.*;
import java.nio.channels.*;
import java.text.*;
import java.util.*;

/**
 */
public class PureJavaDiscoverThreadBottleneck {

	public static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat
("HH:mm:ss:SSS");

	public static final String TEST_FILE_NAME_0 = "iotestdata0.txt";
	public static final String TEST_FILE_NAME_1 = "iotestdata1.txt";
	public static final String TEST_FILE_NAME_2 = "iotestdata2.txt";
	public static final String TEST_FILE_NAME_3 = "iotestdata3.txt";


	public static final long BASIC_IO = 1;
	public static final long BASIC_RW_IO = 2;
	public static final long BASIC_NIO = 4;
	public static final long BASIC_RW_NIO = 8;

	public PureJavaDiscoverThreadBottleneck(long run) {

		if ((run & BASIC_IO) == BASIC_IO) {
			runBasicIO();
		}
		
		if ((run & BASIC_RW_IO) == BASIC_RW_IO) {
			runBasicRWIO();
		}

		if ((run & BASIC_NIO) == BASIC_NIO) {
			runBasicNIO();
		}
		
		if ((run & BASIC_RW_NIO) == BASIC_RW_NIO) {
			runBasicRWNIO();
		}

	}
	

	public void runBasicNIO() {
		
		System.out.println("******** BASIC NIO **********");
		
		File f0 = new File(TEST_FILE_NAME_0);
		File f1 = new File(TEST_FILE_NAME_1);
		File f2 = new File(TEST_FILE_NAME_2);
		File f3 = new File(TEST_FILE_NAME_3);

		
		
		System.out.println("File location is " + f0.getAbsolutePath());
		
		System.out.println("Reading one file:");
		System.out.println("\tStarted: " + DATE_FORMAT.format(new Date
()));
		BasicNIOThread t = new BasicNIOThread(f0);
		t.start();
		try {
			t.join();
		} catch (Exception e) {
			
		}
		
		System.out.println("\tEnded: " + DATE_FORMAT.format(new Date
()));
		System.out.println("Done");

		System.out.println("Reading two sync files:");
		System.out.println("\tStarted: " + DATE_FORMAT.format(new Date
()));

		// begin sync
		t = new BasicNIOThread(f0);
		t.start();
		try {
			t.join();
		} catch (Exception e) {
			
		}

		t = new BasicNIOThread(f1);
		t.start();
		try {
			t.join();
		} catch (Exception e) {
			
		}

		// end sync

		System.out.println("\tEnded: " + DATE_FORMAT.format(new Date
()));


		
		System.out.println("Reading four sync files:");
		System.out.println("\tStarted: " + DATE_FORMAT.format(new Date
()));

		// begin sync
		t = new BasicNIOThread(f0);
		t.start();
		try {
			t.join();
		} catch (Exception e) {
			
		}

		t = new BasicNIOThread(f1);
		t.start();
		try {
			t.join();
		} catch (Exception e) {
			
		}
		t = new BasicNIOThread(f2);
		t.start();
		try {
			t.join();
		} catch (Exception e) {
			
		}
		t = new BasicNIOThread(f3);
		t.start();
		try {
			t.join();
		} catch (Exception e) {
			
		}


		// end sync

		System.out.println("\tEnded: " + DATE_FORMAT.format(new Date
()));
		System.out.println("Done");

		System.out.println("Reading two unsync files:");
		System.out.println("\tStarted: " + DATE_FORMAT.format(new Date
()));
		BasicNIOThread t0 = new BasicNIOThread(f0);
		BasicNIOThread t1 = new BasicNIOThread(f1);

		try {
			t0.start();
			t1.start();
			
			
			t0.join();
			t1.join();
		} catch (Exception e) {
			e.printStackTrace();
		}
		System.out.println("\tEnded: " + DATE_FORMAT.format(new Date
()));

		
		System.out.println("Reading four unsync files:");
		System.out.println("\tStarted: " + DATE_FORMAT.format(new Date
()));
		t0 = new BasicNIOThread(f0);
		t1 = new BasicNIOThread(f1);
		BasicNIOThread t2 = new BasicNIOThread(f2);
		BasicNIOThread t3 = new BasicNIOThread(f3);

		try {
			t0.start();
			t1.start();
			t2.start();
			t3.start();
			
			
			t0.join();
			t1.join();
			t2.join();
			t3.join();
		} catch (Exception e) {
			e.printStackTrace();
		}
		System.out.println("\tEnded: " + DATE_FORMAT.format(new Date
()));
		System.out.println("Done");
		
		
	}











	public void runBasicIO() {
		
		System.out.println("******** BASIC IO **********");
		
		File f0 = new File(TEST_FILE_NAME_0);
		File f1 = new File(TEST_FILE_NAME_1);
		File f2 = new File(TEST_FILE_NAME_2);
		File f3 = new File(TEST_FILE_NAME_3);

		
		
		System.out.println("File location is " + f0.getAbsolutePath());
		
		System.out.println("Reading one file:");
		System.out.println("\tStarted: " + DATE_FORMAT.format(new Date
()));
		BasicIOThread t = new BasicIOThread(f0);
		t.start();
		try {
			t.join();
		} catch (Exception e) {
			
		}
		
		System.out.println("\tEnded: " + DATE_FORMAT.format(new Date
()));
		System.out.println("Done");

		System.out.println("Reading two sync files:");
		System.out.println("\tStarted: " + DATE_FORMAT.format(new Date
()));

		// begin sync
		t = new BasicIOThread(f0);
		t.start();
		try {
			t.join();
		} catch (Exception e) {
			
		}

		t = new BasicIOThread(f1);
		t.start();
		try {
			t.join();
		} catch (Exception e) {
			
		}

		// end sync

		System.out.println("\tEnded: " + DATE_FORMAT.format(new Date
()));


		
		System.out.println("Reading four sync files:");
		System.out.println("\tStarted: " + DATE_FORMAT.format(new Date
()));

		// begin sync
		t = new BasicIOThread(f0);
		t.start();
		try {
			t.join();
		} catch (Exception e) {
			
		}

		t = new BasicIOThread(f1);
		t.start();
		try {
			t.join();
		} catch (Exception e) {
			
		}
		t = new BasicIOThread(f2);
		t.start();
		try {
			t.join();
		} catch (Exception e) {
			
		}
		t = new BasicIOThread(f3);
		t.start();
		try {
			t.join();
		} catch (Exception e) {
			
		}


		// end sync

		System.out.println("\tEnded: " + DATE_FORMAT.format(new Date
()));
		System.out.println("Done");

		System.out.println("Reading two unsync files:");
		System.out.println("\tStarted: " + DATE_FORMAT.format(new Date
()));
		BasicIOThread t0 = new BasicIOThread(f0);
		BasicIOThread t1 = new BasicIOThread(f1);

		try {
			t0.start();
			t1.start();
			
			
			t0.join();
			t1.join();
		} catch (Exception e) {
			e.printStackTrace();
		}
		System.out.println("\tEnded: " + DATE_FORMAT.format(new Date
()));

		
		System.out.println("Reading four unsync files:");
		System.out.println("\tStarted: " + DATE_FORMAT.format(new Date
()));
		t0 = new BasicIOThread(f0);
		t1 = new BasicIOThread(f1);
		BasicIOThread t2 = new BasicIOThread(f2);
		BasicIOThread t3 = new BasicIOThread(f3);

		try {
			t0.start();
			t1.start();
			t2.start();
			t3.start();
			
			
			t0.join();
			t1.join();
			t2.join();
			t3.join();
		} catch (Exception e) {
			e.printStackTrace();
		}
		System.out.println("\tEnded: " + DATE_FORMAT.format(new Date
()));
		System.out.println("Done");
		
		
	}






	public void runBasicRWNIO() {
		
		System.out.println("******** BASIC RW IO **********");
		
		File f0 = new File(TEST_FILE_NAME_0);
		File f1 = new File(TEST_FILE_NAME_1);
		File f2 = new File(TEST_FILE_NAME_2);
		File f3 = new File(TEST_FILE_NAME_3);


		
		System.out.println("File location is " + f0.getAbsolutePath());
		
		System.out.println("Reading one file:");
		System.out.println("\tStarted: " + DATE_FORMAT.format(new Date
()));
		BasicRWNIOThread t = new BasicRWNIOThread(f0,new File
("out.tmp"));
		t.start();
		try {
			t.join();
		} catch (Exception e) {
			
		}
		
		System.out.println("\tEnded: " + DATE_FORMAT.format(new Date
()));
		System.out.println("Done");

		System.out.println("Reading two sync files:");
		System.out.println("\tStarted: " + DATE_FORMAT.format(new Date
()));

		// begin sync
		t = new BasicRWNIOThread(f0,new File("out0.tmp"));
		t.start();
		try {
			t.join();
		} catch (Exception e) {
			
		}

		t = new BasicRWNIOThread(f1,new File("out1.tmp"));
		t.start();
		try {
			t.join();
		} catch (Exception e) {
			
		}

		// end sync

		System.out.println("\tEnded: " + DATE_FORMAT.format(new Date
()));
		System.out.println("Done");

		System.out.println("Reading four sync files:");
		System.out.println("\tStarted: " + DATE_FORMAT.format(new Date
()));

		// begin sync
		t = new BasicRWNIOThread(f0,new File("out0.tmp"));
		t.start();
		try {
			t.join();
		} catch (Exception e) {
			
		}

		t = new BasicRWNIOThread(f1,new File("out1.tmp"));
		t.start();
		try {
			t.join();
		} catch (Exception e) {
			
		}
		t = new BasicRWNIOThread(f2,new File("out2.tmp"));
		t.start();
		try {
			t.join();
		} catch (Exception e) {
			
		}
		t = new BasicRWNIOThread(f3,new File("out3.tmp"));
		t.start();
		try {
			t.join();
		} catch (Exception e) {
			
		}


		// end sync

		System.out.println("\tEnded: " + DATE_FORMAT.format(new Date
()));
		System.out.println("Done");

		System.out.println("Reading two unsync files:");
		System.out.println("\tStarted: " + DATE_FORMAT.format(new Date
()));
		BasicRWNIOThread t0 = new BasicRWNIOThread(f0,new File
("un_out0.tmp"));
		BasicRWNIOThread t1 = new BasicRWNIOThread(f1,new File
("un_out1.tmp"));

		try {
			t0.start();
			t1.start();

			t0.join();
			t1.join();

		} catch (Exception e) {
			e.printStackTrace();
		}
		System.out.println("\tEnded: " + DATE_FORMAT.format(new Date
()));
		System.out.println("Done");

		System.out.println("Reading four unsync files:");
		System.out.println("\tStarted: " + DATE_FORMAT.format(new Date
()));
		t0 = new BasicRWNIOThread(f0,new File("un_out0.tmp"));
		t1 = new BasicRWNIOThread(f1,new File("un_out1.tmp"));
		BasicRWNIOThread t2 = new BasicRWNIOThread(f2,new File
("un_out2.tmp"));
		BasicRWNIOThread t3 = new BasicRWNIOThread(f3,new File
("un_out3.tmp"));

		try {
			t0.start();
			t1.start();
			t2.start();
			t3.start();
			
			
			t0.join();
			t1.join();
			t2.join();
			t3.join();
		} catch (Exception e) {
			e.printStackTrace();
		}
		System.out.println("\tEnded: " + DATE_FORMAT.format(new Date
()));
		System.out.println("Done");
		
		
	}







	public void runBasicRWIO() {
		
		System.out.println("******** BASIC RW IO **********");
		
		File f0 = new File(TEST_FILE_NAME_0);
		File f1 = new File(TEST_FILE_NAME_1);
		File f2 = new File(TEST_FILE_NAME_2);
		File f3 = new File(TEST_FILE_NAME_3);


		
		System.out.println("File location is " + f0.getAbsolutePath());
		
		System.out.println("Reading one file:");
		System.out.println("\tStarted: " + DATE_FORMAT.format(new Date
()));
		BasicRWIOThread t = new BasicRWIOThread(f0,"out.tmp");
		t.start();
		try {
			t.join();
		} catch (Exception e) {
			
		}
		
		System.out.println("\tEnded: " + DATE_FORMAT.format(new Date
()));
		System.out.println("Done");

		System.out.println("Reading two sync files:");
		System.out.println("\tStarted: " + DATE_FORMAT.format(new Date
()));

		// begin sync
		t = new BasicRWIOThread(f0,"out0.tmp");
		t.start();
		try {
			t.join();
		} catch (Exception e) {
			
		}

		t = new BasicRWIOThread(f1,"out1.tmp");
		t.start();
		try {
			t.join();
		} catch (Exception e) {
			
		}

		// end sync

		System.out.println("\tEnded: " + DATE_FORMAT.format(new Date
()));
		System.out.println("Done");

		System.out.println("Reading four sync files:");
		System.out.println("\tStarted: " + DATE_FORMAT.format(new Date
()));

		// begin sync
		t = new BasicRWIOThread(f0,"out0.tmp");
		t.start();
		try {
			t.join();
		} catch (Exception e) {
			
		}

		t = new BasicRWIOThread(f1,"out1.tmp");
		t.start();
		try {
			t.join();
		} catch (Exception e) {
			
		}
		t = new BasicRWIOThread(f2,"out2.tmp");
		t.start();
		try {
			t.join();
		} catch (Exception e) {
			
		}
		t = new BasicRWIOThread(f3,"out3.tmp");
		t.start();
		try {
			t.join();
		} catch (Exception e) {
			
		}


		// end sync

		System.out.println("\tEnded: " + DATE_FORMAT.format(new Date
()));
		System.out.println("Done");

		System.out.println("Reading two unsync files:");
		System.out.println("\tStarted: " + DATE_FORMAT.format(new Date
()));
		BasicRWIOThread t0 = new BasicRWIOThread(f0,"un_out0.tmp");
		BasicRWIOThread t1 = new BasicRWIOThread(f1,"un_out1.tmp");

		try {
			t0.start();
			t1.start();

			t0.join();
			t1.join();

		} catch (Exception e) {
			e.printStackTrace();
		}
		System.out.println("\tEnded: " + DATE_FORMAT.format(new Date
()));
		System.out.println("Done");

		System.out.println("Reading four unsync files:");
		System.out.println("\tStarted: " + DATE_FORMAT.format(new Date
()));
		t0 = new BasicRWIOThread(f0,"un_out0.tmp");
		t1 = new BasicRWIOThread(f1,"un_out1.tmp");
		BasicRWIOThread t2 = new BasicRWIOThread(f2,"un_out2.tmp");
		BasicRWIOThread t3 = new BasicRWIOThread(f3,"un_out3.tmp");

		try {
			t0.start();
			t1.start();
			t2.start();
			t3.start();
			
			
			t0.join();
			t1.join();
			t2.join();
			t3.join();
		} catch (Exception e) {
			e.printStackTrace();
		}
		System.out.println("\tEnded: " + DATE_FORMAT.format(new Date
()));
		System.out.println("Done");
		
		
	}


	// ============================================================ INNER
CLASSES ====


	public class Basi

Comments
This issue is being examined by JDK-6265734.
05-08-2013

EVALUATION This problem appears to be Windows-specific. With the given test program (slightly modified to print elapsed rather than absolute times, see attachment) I've measured near-linear speedups using 1.4.1 on both solaris-sparc (on an eight-way E4500) and linux-i486 (on a four-way 700MHz P3). This bug could be an artifact of the Windows threading implementation. -- ###@###.### 2002/12/2
12-10-0168