java io顺序_Java顺序IO性能
java io順序
許多應(yīng)用程序?qū)⒁幌盗惺录涗浀交谖募拇鎯?chǔ)中,以供以后使用。 從日志記錄和審核,直到在事件源設(shè)計(jì)或其緊密相關(guān)的CQRS中保留事務(wù)重做日志,這都可以是任何東西。 Java具有多種方法,可以通過(guò)這些方法將文件順序?qū)懭牖蛑匦伦x取。 本文探討了其中一些機(jī)制,以了解其性能特征。 對(duì)于本文的范圍,我將使用預(yù)分配的文件,因?yàn)槲蚁腙P(guān)注性能。 不斷擴(kuò)展文件會(huì)帶來(lái)很大的性能開銷,并給應(yīng)用程序增加抖動(dòng),從而導(dǎo)致高度可變的延遲。 “為什么預(yù)分配的文件性能更好?”,我聽到您問(wèn)。 好吧,在磁盤上,文件是由一系列包含數(shù)據(jù)的塊/頁(yè)面組成的。 首先,重要的是這些塊是連續(xù)的,以提供快速的順序訪問(wèn)。 其次,必須分配元數(shù)據(jù)來(lái)描述此文件在磁盤上并保存在文件系統(tǒng)中。 典型的大文件將分配許多“間接”塊,以描述包含組成此元數(shù)據(jù)一部分的文件內(nèi)容的數(shù)據(jù)塊鏈。 我將其留給讀者或以后的文章來(lái)練習(xí),以探討不預(yù)先分配數(shù)據(jù)文件對(duì)性能的影響。 如果您使用過(guò)數(shù)據(jù)庫(kù),則可能已經(jīng)注意到它預(yù)先分配了所需的文件。 考試 我想嘗試2種文件大小。 一個(gè)足夠大,可以測(cè)試順序訪問(wèn),但可以輕松放入文件系統(tǒng)緩存中;另一個(gè)很大,可以使緩存子系統(tǒng)被迫退出頁(yè)面,以便可以加載新頁(yè)面。 對(duì)于這兩種情況,我將分別使用400MB和8GB。 我還將遍歷文件多次,以顯示預(yù)熱和預(yù)熱特性。 我將測(cè)試4種順序?qū)懭牒妥x取文件的方式:代碼
import java.io.*; import java.nio.ByteBuffer; import java.nio.MappedByteBuffer; import java.nio.channels.FileChannel;import static java.lang.Integer.MAX_VALUE; import static java.lang.System.out; import static java.nio.channels.FileChannel.MapMode.READ_ONLY; import static java.nio.channels.FileChannel.MapMode.READ_WRITE;public final class TestSequentialIoPerf {public static final int PAGE_SIZE = 1024 * 4;public static final long FILE_SIZE = PAGE_SIZE * 2000L * 1000L;public static final String FILE_NAME = "test.dat";public static final byte[] BLANK_PAGE = new byte[PAGE_SIZE];public static void main(final String[] arg) throws Exception{preallocateTestFile(FILE_NAME);for (final PerfTestCase testCase : testCases){for (int i = 0; i < 5; i++){System.gc();long writeDurationMs = testCase.test(PerfTestCase.Type.WRITE,FILE_NAME);System.gc();long readDurationMs = testCase.test(PerfTestCase.Type.READ,FILE_NAME);long bytesReadPerSec = (FILE_SIZE * 1000L) / readDurationMs;long bytesWrittenPerSec = (FILE_SIZE * 1000L) / writeDurationMs;out.format("%s\twrite=%,d\tread=%,d bytes/sec\n",testCase.getName(),bytesWrittenPerSec, bytesReadPerSec);}}deleteFile(FILE_NAME);}private static void preallocateTestFile(final String fileName)throws Exception{RandomAccessFile file = new RandomAccessFile(fileName, "rw");for (long i = 0; i < FILE_SIZE; i += PAGE_SIZE){file.write(BLANK_PAGE, 0, PAGE_SIZE);}file.close();}private static void deleteFile(final String testFileName) throws Exception{File file = new File(testFileName);if (!file.delete()){out.println("Failed to delete test file=" + testFileName);out.println("Windows does not allow mapped files to be deleted.");}}public abstract static class PerfTestCase{public enum Type { READ, WRITE }private final String name;private int checkSum;public PerfTestCase(final String name){this.name = name;}public String getName(){return name;}public long test(final Type type, final String fileName){long start = System.currentTimeMillis();try{switch (type){case WRITE:{checkSum = testWrite(fileName);break;}case READ:{final int checkSum = testRead(fileName);if (checkSum != this.checkSum){final String msg = getName() +" expected=" + this.checkSum +" got=" + checkSum;throw new IllegalStateException(msg);}break;}}}catch (Exception ex){ex.printStackTrace();}return System.currentTimeMillis() - start;}public abstract int testWrite(final String fileName) throws Exception;public abstract int testRead(final String fileName) throws Exception;}private static PerfTestCase[] testCases ={new PerfTestCase("RandomAccessFile"){public int testWrite(final String fileName) throws Exception{RandomAccessFile file = new RandomAccessFile(fileName, "rw");final byte[] buffer = new byte[PAGE_SIZE];int pos = 0;int checkSum = 0;for (long i = 0; i < FILE_SIZE; i++){byte b = (byte)i;checkSum += b;buffer[pos++] = b;if (PAGE_SIZE == pos){file.write(buffer, 0, PAGE_SIZE);pos = 0;}}file.close();return checkSum;}public int testRead(final String fileName) throws Exception{RandomAccessFile file = new RandomAccessFile(fileName, "r");final byte[] buffer = new byte[PAGE_SIZE];int checkSum = 0;int bytesRead;while (-1 != (bytesRead = file.read(buffer))){for (int i = 0; i < bytesRead; i++){checkSum += buffer[i];}}file.close();return checkSum;}},new PerfTestCase("BufferedStreamFile"){public int testWrite(final String fileName) throws Exception{int checkSum = 0;OutputStream out = new BufferedOutputStream(new FileOutputStream(fileName));for (long i = 0; i < FILE_SIZE; i++){byte b = (byte)i;checkSum += b;out.write(b);}out.close();return checkSum;}public int testRead(final String fileName) throws Exception{int checkSum = 0;InputStream in = new BufferedInputStream(new FileInputStream(fileName));int b;while (-1 != (b = in.read())){checkSum += (byte)b;}in.close();return checkSum;}},new PerfTestCase("BufferedChannelFile"){public int testWrite(final String fileName) throws Exception{FileChannel channel = new RandomAccessFile(fileName, "rw").getChannel();ByteBuffer buffer = ByteBuffer.allocate(PAGE_SIZE);int checkSum = 0;for (long i = 0; i < FILE_SIZE; i++){byte b = (byte)i;checkSum += b;buffer.put(b);if (!buffer.hasRemaining()){buffer.flip();channel.write(buffer);buffer.clear();}}channel.close();return checkSum;}public int testRead(final String fileName) throws Exception{FileChannel channel = new RandomAccessFile(fileName, "rw").getChannel();ByteBuffer buffer = ByteBuffer.allocate(PAGE_SIZE);int checkSum = 0;while (-1 != (channel.read(buffer))){buffer.flip();while (buffer.hasRemaining()){checkSum += buffer.get();}buffer.clear();}return checkSum;}},new PerfTestCase("MemoryMappedFile"){public int testWrite(final String fileName) throws Exception{FileChannel channel = new RandomAccessFile(fileName, "rw").getChannel();MappedByteBuffer buffer = channel.map(READ_WRITE, 0,Math.min(channel.size(), MAX_VALUE));int checkSum = 0;for (long i = 0; i < FILE_SIZE; i++){if (!buffer.hasRemaining()){buffer = channel.map(READ_WRITE, i,Math.min(channel.size() - i , MAX_VALUE));}byte b = (byte)i;checkSum += b;buffer.put(b);}channel.close();return checkSum;}public int testRead(final String fileName) throws Exception{FileChannel channel = new RandomAccessFile(fileName, "rw").getChannel();MappedByteBuffer buffer = channel.map(READ_ONLY, 0,Math.min(channel.size(), MAX_VALUE));int checkSum = 0;for (long i = 0; i < FILE_SIZE; i++){if (!buffer.hasRemaining()){buffer = channel.map(READ_WRITE, i,Math.min(channel.size() - i , MAX_VALUE));}checkSum += buffer.get();}channel.close();return checkSum;}},}; }結(jié)果
400MB file
===========
RandomAccessFile write=379,610,750 read=1,452,482,269 bytes/sec
BufferedStreamFile寫入= 98,178,331讀取= 286,433,566字節(jié)/秒
BufferedStreamFile寫入= 100,244,738讀取= 288,857,545字節(jié)/秒
BufferedStreamFile寫入= 82,948,562讀取= 154,100,827字節(jié)/秒 BufferedStreamFile寫入= 108,503,311讀取= 153,869,271字節(jié)/秒 BufferedStreamFile寫入= 113,055,478讀取= 152,608,047字節(jié)/秒
BufferedChannelFile寫入= 228,443,948讀取= 356,173,913字節(jié)/秒
BufferedChannelFile寫入= 265,629,053讀取= 374,063,926字節(jié)/秒
BufferedChannelFile寫= 223,825,136讀= 1,539,849,624字節(jié)/秒BufferedChannelFile寫= 232,992,036讀= 1,539,849,624字節(jié)/秒BufferedChannelFile寫= 212,779,220讀= 1,534,082,397字節(jié)/秒 MemoryMappedFile寫入= 300,955,180讀取= 305,899,925字節(jié)/秒 MemoryMappedFile寫入= 313,149,847讀取= 310,538,286字節(jié)/秒 MemoryMappedFile寫入= 326,374,501讀取= 303,857,566字節(jié)/秒 MemoryMappedFile寫入= 327,680,000讀取= 304,535,315字節(jié)/秒 MemoryMappedFile寫入= 326895450讀取= 303632320字節(jié)/秒
8GB文件
============
RandomAccessFile寫入= 167,402,321讀取= 251,922,012字節(jié)/秒 RandomAccessFile寫入= 193,934,802讀取= 257,052,307字節(jié)/秒 RandomAccessFile寫入= 192,948,159讀取= 248,460,768字節(jié)/秒 RandomAccessFile寫入= 191,814,180讀取= 245,225,408字節(jié)/秒 RandomAccessFile寫入= 190,635,762讀取= 275,315,073字節(jié)/秒
BufferedStreamFile寫入= 154,823,102讀取= 248,355,313字節(jié)/秒
BufferedStreamFile寫入= 152,083,913讀取= 253,418,301字節(jié)/秒
BufferedStreamFile寫入= 133,099,369讀取= 146,056,197字節(jié)/秒 BufferedStreamFile write = 131,065,708 read = 146,217,827字節(jié)/秒 BufferedStreamFile寫入= 132694052讀取= 148116004字節(jié)/秒
BufferedChannelFile寫入= 186,703,740讀取= 215,075,218字節(jié)/秒
BufferedChannelFile寫入= 190,591,410讀取= 211,030,680字節(jié)/秒BufferedChannelFile寫入= 187,220,038讀取= 223,087,606字節(jié)/秒
BufferedChannelFile寫入= 191,585,397讀取= 221,297,747字節(jié)/秒 BufferedChannelFile寫入= 192,653,214讀取= 211,789,038字節(jié)/秒
MemoryMappedFile寫入= 123,023,322讀取= 231,530,156字節(jié)/秒
MemoryMappedFile寫入= 121,961,023讀取= 230,403,600字節(jié)/秒
MemoryMappedFile寫入= 123,317,778讀取= 229,899,250字節(jié)/秒 MemoryMappedFile寫入= 121,472,738讀取= 231,739,745字節(jié)/秒 MemoryMappedFile寫入= 120,362,615讀取= 231,190,382字節(jié)/秒
分析
多年來(lái),我一直是直接使用RandomAccessFile的忠實(shí)擁護(hù)者 ,因?yàn)樗峁┝丝刂坪涂深A(yù)測(cè)的執(zhí)行。 從性能的角度來(lái)看,我從來(lái)沒(méi)有發(fā)現(xiàn)使用緩沖流會(huì)很有用,而且情況似乎仍然如此。 在最近的測(cè)試中,我發(fā)現(xiàn)使用NIO FileChannel和ByteBuffer做得更好。 使用Java 7,此編程方法的靈活性已得到改善,可以使用SeekableByteChannel進(jìn)行隨機(jī)訪問(wèn)。 似乎在某些情況下,讀取RandomAccessFile和NIO可以很好地使Memory Mapped文件贏得寫操作。 我看到這些結(jié)果因平臺(tái)而異。 文件系統(tǒng),操作系統(tǒng),存儲(chǔ)設(shè)備和可用內(nèi)存都會(huì)產(chǎn)生重大影響。 在某些情況下,我已經(jīng)看到內(nèi)存映射文件的性能明顯優(yōu)于其他文件,但這需要在您的平臺(tái)上進(jìn)行測(cè)試,因?yàn)槟睦锍炭赡軙?huì)有所不同…… 在推動(dòng)最大吞吐量時(shí),應(yīng)特別注意使用內(nèi)存映射的大文件。 我經(jīng)常發(fā)現(xiàn)操作系統(tǒng)可能由于虛擬內(nèi)存子系統(tǒng)上的壓力而變得無(wú)響應(yīng)。 結(jié)論 從Java執(zhí)行順序文件IO的不同方法在性能上存在顯著差異。 并非所有方法都遙遙相等。 對(duì)于大多數(shù)IO,我發(fā)現(xiàn)使用ByteBuffers和Channels是IO庫(kù)中最優(yōu)化的部分。 如果緩沖流是您的IO庫(kù)的選擇,那么值得進(jìn)行分支并熟悉Channel和Buffer的實(shí)現(xiàn),甚至可以使用舊的RandomAccessFile進(jìn)行回退。 參考: Mechanical Sympathy博客上的JCG合作伙伴 Martin Thompson提供的Java順序IO性能 。翻譯自: https://www.javacodegeeks.com/2012/07/java-sequential-io-performance.html
java io順序
總結(jié)
以上是生活随笔為你收集整理的java io顺序_Java顺序IO性能的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 人员身份怎么填写 人员身份如何填写
- 下一篇: Java EE 8,当前状态是什么:自2