List Operation Performance

These days, i have encountered a problem about the list Operation Performance, the scenario is below: one connection gets the record keys, while other threads get the all attributes of the record according the key, then save these records into files. and the number of  the record attributes is about 100~200 and the amount of the records could reach about ten million. so the points to enhance the performance is located in several directions:

 (1) Reduce the scope of the Test,  it is a very long time to list all the ten million records, according the past experience, the time is about 10 hours, so should reduce the test scope, such as list only 10000, 10 0000, 50 0000, 100 0000, all, these partitions.

 (2) the proper numbers of threads, in order to fix the proper thread numbers, it should test based on the ability of the Data Base.

 (3) How to reduce the unnecessary synchronize block,  from the scenario it is obvious that the blocking queue should be took precedence, of course, the possibility that the customized queue has better performance should also been taken consider. Some well-known queues are ArrayBlockingQueue, LinkedBlockingQueue, SynchronusQueue, ,ConcurrentLinkedQueue and the common stack, the former three are the blocking queue, could be directly used, while the latter two should be wrapped using the synchronized block like the 'Consumer-Producer Model'.  DO a test to list about 10 0000, the time cost:

     ArrayBlocking~    LinkedBlocking~  Synchronus~  ConcurrentLinked~   common stack

          10m24s                    16m0s            10m35s             11m6s                           10m29s

  From the Result that the ArrayBlockingQueue  has the best performance,  although the Java doc says that "Linked queues typically have higher throughput than array-based queues but less predictable performance in most concurrent applications."  I have thought that LinkedBlockingQueue  has more performance than  ArrayBlockingQueue     because it is need to frequently insert/get a record to a queue, but the result looks that it is converse.and SynchronusQueue  also has a better performance.

 (4) save one records to the file. IF all the records should be saved in a file or sequential files with fixed size,  then the write operation should be synchronized, otherwise, one connection getting records could have its own file to write. and all the attributes of the record should be wrapped in a string then to use the BufferedWrite(File Writer) to save the content. 

import java.io.BufferedOutputStream;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileOutputStream;
import java.io.FileWriter;

public class TestTxtWriter {

	public static void main(String[] args) {
		FileOutputStream out = null;
		FileOutputStream outSTr = null;
		BufferedOutputStream Buff = null;
		FileWriter fw = null;
		BufferedWriter bw=null;
		int count = 3000000;
		try {
			out = new FileOutputStream(new File("D:/FileOutputStream.txt"));
			long begin = System.currentTimeMillis();
			for (int i = 0; i < count; i++) {
				out.write("FileOutputStream,BufferedOutputStream,FileWriter and BufferedWriter,write Operation\r\n".getBytes());
			}
			out.close();
			long end = System.currentTimeMillis();
			System.out.println("FileOutputStream time cost:" + (end - begin) + " ms");

			outSTr = new FileOutputStream(new File("D:/BufferedOutputStream.txt"));
			Buff = new BufferedOutputStream(outSTr);
			long begin0 = System.currentTimeMillis();
			for (int i = 0; i < count; i++) {
				Buff.write("FileOutputStream,BufferedOutputStream,FileWriter and BufferedWriter,write Operation\r\n".getBytes());
			}
			Buff.flush();
			Buff.close();
			long end0 = System.currentTimeMillis();
			System.out.println("BufferedOutputStream time cost:" + (end0 - begin0)
					+ " ms");

			fw = new FileWriter("D:/FileWriter.txt");
			long begin3 = System.currentTimeMillis();
			for (int i = 0; i < count; i++) {
				fw.write("FileOutputStream,BufferedOutputStream,FileWriter and BufferedWriter,write Operation\r\n");
			}
			fw.close();
			long end3 = System.currentTimeMillis();
			System.out.println("FileWriter time cost:" + (end3 - begin3) + " ms");
			
			bw =new BufferedWriter(new FileWriter("D:/FileBuffer.txt"));
			
			long begin4 = System.currentTimeMillis();
			for (int i = 0; i < count; i++) {
				bw.write("FileOutputStream,BufferedOutputStream,FileWriter and BufferedWriter,write Operation\r\n");
			}
			bw.flush();
			bw.close();
			long end4 = System.currentTimeMillis();
			System.out.println("BufferedWriter time cost:" + (end4 - begin4) + " ms");

		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			try {
				fw.close();
				Buff.close();
				outSTr.close();
				out.close();
				bw.close();
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
		
		/**
		 Count =100 0000;
		 FileOutputStream time cost:21077 ms
         BufferedOutputStream time cost:5359 ms
         FileWriter time cost:3485 ms
         BufferedWriter time cost:3609 ms
         from the above the BufferedWriter is not always better
         
        Count =300 0000
        FileOutputStream time cost:58998 ms
        BufferedOutputStream time cost:7875 ms
        FileWriter time cost:6843 ms
        BufferedWriter time cost:6640 ms
        the BufferedWriter performance begins getting better than FileWriter
		 */
	}
}

import java.io.File;
import java.io.FileWriter;

public class TestWriteSyn extends Thread{
	
	public WriteFillClass writFl;
	public void setFileName(WriteFillClass writFl){
		this.writFl=writFl;
	}
	
	public void run(){
		writFl.write();
	}

	public static void main(String[] args) {
		long one=writeOneFileTwoThreads();
		System.out.println("writeOneFileTwoThreads="+(one)+"ms");
		long two=writeTwoFileTwoThreads();
		System.out.println("writeTwoFileTwoThreads="+(two)+"ms");
		
		if(two>one){
			System.out.println("writeTwoFileTwoThreads > writeOneFileTwoThreads ms");
		}else{
			System.out.println("writeTwoFileTwoThreads =< writeOneFileTwoThreads ms");
		}
		/**
		 writeOneFileTwoThreads=31ms
         writeTwoFileTwoThreads=16ms
         writeTwoFileTwoThreads =< writeOneFileTwoThreads ms
		 */
	}
	
	private static long writeOneFileTwoThreads(){
		
       long begin3 = System.currentTimeMillis();
		
		try {
			TestWriteSyn one =new TestWriteSyn();
			TestWriteSyn two =new TestWriteSyn();
			
			WriteFillClass writeFile=new WriteFillClass();
			writeFile.setFileName("1");
			
			one.setFileName(writeFile);
			one.start();

			two.setFileName(writeFile);
			two.start();
			
			one.join();
			two.join();
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	
		long end3 = System.currentTimeMillis();
	    return end3-begin3;
		
	}
	
	private static long writeTwoFileTwoThreads(){
		
	       long begin3 = System.currentTimeMillis();
			
			try {
				TestWriteSyn one =new TestWriteSyn();
				TestWriteSyn two =new TestWriteSyn();
				
				WriteFillClass writeFile=new WriteFillClass();
				writeFile.setFileName("2");
				
				one.setFileName(writeFile);
				one.start();

				WriteFillClass writeFile1=new WriteFillClass();
				writeFile1.setFileName("3");
				
				two.setFileName(writeFile1);
				two.start();
				
				one.join();
				two.join();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		
			long end3 = System.currentTimeMillis();
			return end3-begin3;
		}
	
	static class WriteFillClass{
		/**
		 If no static:
		 No enclosing instance of type TestWriteSyn is accessible. 
		 Must qualify the allocation with an enclosing instance of type TestWriteSyn 
		 (e.g. x.new A() where x is an instance of TestWriteSyn).
		 */
		
		public String fileName;
		public void setFileName(String fileNa){
			fileName=fileNa;
		}
		
		public synchronized void write(){
			
			FileWriter fw = null;
			int count = 3000;
			try {
				String filePath="D:/FileWriter"+fileName+".txt";
				File filell=new File(filePath);
				if(filell.exists()){
					filell.delete();
				}
				StringBuffer one=new StringBuffer();
				for (int i = 0; i < count; i++) {
					one.append("FileOutputStream,BufferedOutputStream,FileWriter and BufferedWriter,write Operation\r\n");
				}
							
				fw = new FileWriter(filePath);
				fw.write(one.toString());
				fw.close();

			} catch (Exception e) {
				e.printStackTrace();
			} finally {
				try {
					fw.close();
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
		}
		
	}

}

(5) Use StringBuffer(),  It should be taken considered that the StringBuffer not String should be used. and StringBuffer.append(one+" is value"+two+"onn") not same with StringBuffer.append(one).append("is value").append(two).append("onn"), the former one has more worse performance than the latter one. Still Two points: Firstly, if the StringBuffer is not set Capacity, then its default capacity is 16, it the capacity overflows, it would be made larger. so if it is better that set a proper capacity to the StringBuffer if the capacity could be predicted. Secondly, As of release JDK 5, this class has been supplemented with an equivalent class designed for use by a single thread,StringBuilder. The StringBuilderclass should generally be used in preference to StringBuffer, as it supports all of the same operations but it is faster, as it performs no synchronization.

(6) Use the temp to cache the varibles returned the list.get() rather than use list.get() many times in a loop. 

public class TestReadArrayList {

	public static void main(String[] args) {
		int count=500000;
		ArrayList<String> list=new ArrayList<String>();
		for(int i=0;i<count;i++){
			list.add("OK"+i);
		}
		long begin0 = System.currentTimeMillis();
		for(int i=0;i<count;i++){
			String on=list.get(i)+list.get(i)+list.get(i)+list.get(i)+list.get(i)+list.get(i);
		}
		long end0 = System.currentTimeMillis();
		System.out.println("get(i) cost:" + (end0 - begin0) + "ms");
		
		
		long begin1 = System.currentTimeMillis();
		for(int i=0;i<count;i++){
			String tmp=list.get(i);
			String on=tmp+tmp+tmp+tmp+tmp+tmp;
		}
		long end1 = System.currentTimeMillis();
		System.out.println("temp cost:" + (end1 - begin1) + "ms");
		
	/**
	 get(i) cost:390ms
     temp cost:234ms
     so the result is that use the tmp is more efficient
	 */

	}


BTW, some Remarks:

SynchronousQueue is a very special kind of queue - it implements a rendezvous approach (producer waits until consumer is ready, consumer waits until producer is ready) behind the interface of Queue.

Therefore you may need it only in the special cases when you need that particular semantics, for example, Single threading a task without queuing further requests.

Another reason for using SynchronousQueue is performance. Implementation of SynchronousQueue seems to be heavily optimized, so if you don't need anything more than a rendezvous point (as in the case of Executors.newCachedThreadPool(), where consumers are created "on-demand", so that queue items don't accumulate), you can get a performance gain by using SynchronousQueue.

Simple synthetic test shows that in a simple single producer - single consumer scenario on dual-core machine throughput ofSynchronousQueue is ~20 time higher that throughput of LinkedBlockingQueue and ArrayBlockingQueue with queue length = 1. When queue length is increased, their throughput rises and almost reaches throughput of SynchronousQueue. It means thatSynchronousQueue has low synchronization overhead on multi-core machines compared to other queues. But again, it matters only in specific circumstances when you need a rendezvous point disguised as Queue.


评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值