package io;
import java.io.File;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.channels.FileChannel.MapMode;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* Java NIO(內存映射文件) 與 傳統IO 讀取 性能測試
*
* 讀取 RandomAccessFile 類讀寫測試及其性能優化(一)
* (鏈接 http://jackyin5918.iteye.com/blog/2022888 )中
* GenerateIntArray 生成的並保存到文件的數據.
* 測試時讀取1千萬個整型構成的二進制文件38.1M
*
* 由測試結果分析可知:
*
* readData(f) -- 普通IO方式讀取
* readDataNIO(f) -- NIO Channel,Buffer方式讀取
* readDataNIO_D(f) -- NIO中Buffer使用 直接方式分配空間(allocateDirect)
* readDataMap_M(f) -- 多線程中使用 內存映射文件
* readDataMap(f) -- 單線程中使用內存映射文件讀取
*
* NIO方式使用直接分配Buffer空間方式讀取性能最好,甚至超過了內存映射文件
* (可能是文件還不夠大,如果文件達到1000M以上應該是內存映射文件性能最好).
*
* 普通NIO方式 耗時 是NIO_D方式的2倍.
*
* 傳統IO方式讀取文件,耗時 是NIO的 上百倍.
*
* 內存映射文件,在文件較大(大小達到幾十M時)可以顯著提升性能.
* 多線程中使用內存映射文件 沒有 提高性能.
----------------------測試結果---------
count = 1000, size = 10000
正在讀取數據,請稍後...
readData(f) 讀取數據成功, 耗時:411326
正在讀取數據,請稍後...
readDataNIO_D(f) 讀取數據成功, 耗時:4161
正在讀取數據,請稍後...
readDataNIO(f) 讀取數據成功, 耗時:10645
正在讀取數據,請稍後...
readDataMap_M(f) 讀取數據成功, 耗時:6682
正在讀取數據,請稍後...
readDataMap 讀取數據成功, 耗時:4841
*/
public class GetTheMiddle
{
private int count = 10; // 數組的個數,
private int size = 1000; // 每個數組的元素個數
private int[][] dataArr;
public GetTheMiddle()
{
dataArr = new int[count][size];
}
public GetTheMiddle(int count, int size)
{
this.count = count;
this.size = size;
this.dataArr = new int[count][size];
}
public int[][] getDataArr()
{
return dataArr;
}
public int[][] readData(File f)
{
try
{
RandomAccessFile rf = new RandomAccessFile(f, "r");
for (int i = 0; i < count; i++)
{
for (int j = 0; j < size; j++)
{
dataArr[i][j] = rf.readInt();
}
}
rf.close();
}
catch (Exception e)
{
e.printStackTrace();
}
return dataArr;
}
public int[][] readDataNIO(File f)
{
try
{
RandomAccessFile rf = new RandomAccessFile(f, "r");
FileChannel fc = rf.getChannel();
ByteBuffer buffer = ByteBuffer.allocate(size * 4);
for (int i = 0; i < count; i++)
{
fc.read(buffer);
buffer.rewind();
for (int j = 0; j < size; j++)
{
dataArr[i][j] = buffer.getInt();
}
buffer.rewind();
}
rf.close();
}
catch (Exception e)
{
e.printStackTrace();
}
return dataArr;
}
public int[][] readDataNIO_D(File f)
{
try
{
RandomAccessFile rf = new RandomAccessFile(f, "r");
FileChannel fc = rf.getChannel();
ByteBuffer buffer = ByteBuffer.allocateDirect(size * 4);
for (int i = 0; i < count; i++)
{
fc.read(buffer);
buffer.rewind();
for (int j = 0; j < size; j++)
{
dataArr[i][j] = buffer.getInt();
}
buffer.rewind();
}
rf.close();
}
catch (Exception e)
{
e.printStackTrace();
}
return dataArr;
}
public int[][] readDataMap(File f)
{
try
{
RandomAccessFile rf = new RandomAccessFile(f, "r");
FileChannel fc = rf.getChannel();
int mapSize = size * 4;
for (int i = 0; i < count; i++)
{
int position = i * size * 4;
MappedByteBuffer mBuffer = fc.map(MapMode.READ_ONLY,position,mapSize);
for (int j = 0; j < size; j++)
{
dataArr[i][j] = mBuffer.getInt();
}
}
rf.close();
}
catch (Exception e)
{
e.printStackTrace();
}
return dataArr;
}
class ReadDataTask implements Runnable
{
private File f;
private int dataIndex;
public ReadDataTask(File f,int dataIndex)
{
this.f = f;
this.dataIndex = dataIndex;
}
@Override
public void run()
{
try
{
RandomAccessFile rf = new RandomAccessFile(f, "r");
FileChannel fc = rf.getChannel();
int mapSize = size * 4;
int position = dataIndex * size * 4;
MappedByteBuffer mBuffer = fc.map(MapMode.READ_ONLY,position,mapSize);
for (int j = 0; j < size; j++)
{
dataArr[dataIndex][j] = mBuffer.getInt();
}
rf.close();
}
catch (Exception e)
{
e.printStackTrace();
}
}
}
public int[][] readDataMap_M(File f)
{
try
{
ExecutorService exec = Executors.newCachedThreadPool();
for(int i=0;i<count;i++)
{
exec.execute(new ReadDataTask(f,i));
}
exec.shutdown();
while(true)
{
if(exec.isTerminated()) break;
}
}
catch (Exception e)
{
e.printStackTrace();
}
return dataArr;
}
public static void main(String[] args)
{
int count = 1000;
int size = 10000;
boolean bPrintData = false; // 是否打印生成的數組,當數據量大是不打印,只在小數據量時打印以便測試
boolean bRefreshData = true; //是否重新生成數據,第一次測試時生成數據後,改成false不必重新生成數據
System.out.printf("count = %d, size = %d \n\n", count, size);
GetTheMiddle gm = new GetTheMiddle(count,size);
GenerateIntArray generator = new GenerateIntArray(count, size);
File f;
try
{
f = new File("D:\\D\\test_data.dat");
if(bRefreshData)
{
generator.refreshDataArr();
generator.writeData2File_B(f);
}
System.out.println("正在讀取數據,請稍後...");
long startTmie = System.nanoTime();
gm.readData(f);
long totalTime = (System.nanoTime() - startTmie)/ 100000;
System.out.println("readData(f) 讀取數據成功, 耗時:" + totalTime);
System.out.println();
System.out.println("正在讀取數據,請稍後...");
startTmie = System.nanoTime();
gm.readDataNIO_D(f);
totalTime = (System.nanoTime() - startTmie)/ 100000;
System.out.println("readDataNIO_D(f) 讀取數據成功, 耗時:" + totalTime);
System.out.println();
System.out.println("正在讀取數據,請稍後...");
startTmie = System.nanoTime();
gm.readDataNIO(f);
totalTime = (System.nanoTime() - startTmie)/ 100000;
System.out.println("readDataNIO(f) 讀取數據成功, 耗時:" + totalTime);
System.out.println();
System.out.println("正在讀取數據,請稍後...");
startTmie = System.nanoTime();
gm.readDataMap_M(f);
totalTime = (System.nanoTime() - startTmie)/ 100000;
System.out.println("readDataMap_M(f) 讀取數據成功, 耗時:" + totalTime);
System.out.println();
System.out.println("正在讀取數據,請稍後...");
startTmie = System.nanoTime();
gm.readDataMap(f);
totalTime = (System.nanoTime() - startTmie)/ 100000;
System.out.println("readDataMap 讀取數據成功, 耗時:" + totalTime);
System.out.println();
}
catch (Exception e)
{
e.printStackTrace();
}
if(bPrintData)
{
System.out.println("generator中生成的數據...");
int[][] intArr = generator.getDataArr();
for (int i = 0; i < count; i++)
{
for (int j = 0; j < size; j++)
{
System.out.printf("%1$3d", intArr[i][j]);
}
System.out.println();
}
System.out.println("讀取出來的數組...");
intArr = gm.getDataArr();
for (int i = 0; i < count; i++)
{
for (int j = 0; j < size; j++)
{
System.out.printf("%1$-5s", intArr[i][j]);
}
System.out.println();
}
}
}
}