Java順序IO性能

許多應用程序將一系列事件記錄到基于文件的存儲中,以供以后使用。 從日志記錄和審核,直到在事件源設計或其緊密相關的CQRS中保留事務重做日志,這都可以是任何東西。
Java具有多種方法,可以通過這些方法將文件順序寫入或重新讀取。 本文探討了其中一些機制,以了解其性能特征。 對于本文的范圍,我將使用預分配的文件,因為我想關注性能。 不斷擴展文件會帶來很大的性能開銷,并給應用程序增加抖動,從而導致高度可變的延遲。 “為什么預分配的文件性能更好?”,我聽到您問。 好吧,在磁盤上,文件是由一系列包含數據的塊/頁面組成的。 首先,重要的是這些塊是連續的,以提供快速的順序訪問。 其次,必須分配元數據來描述此文件在磁盤上并保存在文件系統中。 典型的大文件將分配許多“間接”塊,以描述包含組成該元數據一部分的文件內容的數據塊鏈。 我將其留給讀者或以后的文章來練習,以探討不預先分配數據文件對性能的影響。 如果您使用過數據庫,則可能已經注意到它預先分配了所需的文件。
考試
我想嘗試2種文件大小。 一個足夠大,可以測試順序訪問,但可以輕松放入文件系統緩存中;另一個更大,可以使緩存子系統被迫退出頁面,以便可以加載新頁面。 對于這兩種情況,我將分別使用400MB和8GB。 我還將遍歷文件多次以顯示預熱和預熱特性。
我將測試4種順序寫入和讀取文件的方式:
  1. 使用頁面大小的普通字節[]的RandomAccessFile 。
  2. 緩沖的FileInputStream和FileOutputStream 。
  3. 具有頁面大小的ByteBuffer的NIO FileChannel 。
  4. 使用NIO和直接MappedByteBuffer映射文件的內存。
這些測試在具有8GB RAM的2.0Ghz Sandybridge CPU,具有ext4文件系統的Fedora Core 15 64位Linux上的Intel 320 SSD和Oracle JDK 1.6.0_30上運行。

編碼

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;}},};
}

結果

400MB file
===========
RandomAccessFile write=294,041,636 read=1,494,890,510 bytes/sec

BufferedStreamFile寫入= 98,178,331讀取= 286,433,566字節/秒
BufferedStreamFile寫入= 100,244,738讀取= 288,857,545字節/秒
BufferedStreamFile寫入= 82,948,562讀取= 154,100,827字節/秒 BufferedStreamFile寫入= 108,503,311讀取= 153,869,271字節/秒 BufferedStreamFile寫入= 113,055,478讀取= 152,608,047字節/秒

BufferedChannelFile寫入= 228,443,948讀取= 356,173,913字節/秒
BufferedChannelFile寫入= 265,629,053讀取= 374,063,926字節/秒
BufferedChannelFile寫= 223,825,136讀= 1,539,849,624字節/秒BufferedChannelFile寫= 232,992,036讀= 1,539,849,624字節/秒BufferedChannelFile寫= 212,779,220讀= 1,534,082,397字節/秒 MemoryMappedFile寫入= 300,955,180讀取= 305,899,925字節/秒 MemoryMappedFile寫入= 313,149,847讀取= 310,538,286字節/秒 MemoryMappedFile寫入= 326,374,501讀取= 303,857,566字節/秒 MemoryMappedFile寫入= 327,680,000讀取= 304,535,315字節/秒 MemoryMappedFile寫入= 326895450讀取= 303632320字節/秒

8GB文件
============
RandomAccessFile寫入= 167,402,321讀取= 251,922,012字節/秒 RandomAccessFile寫入= 193,934,802讀取= 257,052,307字節/秒 RandomAccessFile寫入= 192,948,159讀取= 248,460,768字節/秒 RandomAccessFile寫入= 191,814,180讀取= 245,225,408字節/秒 RandomAccessFile寫入= 190,635,762讀取= 275,315,073字節/秒

BufferedStreamFile寫入= 154,823,102讀取= 248,355,313字節/秒
BufferedStreamFile寫入= 152,083,913讀取= 253,418,301字節/秒
BufferedStreamFile寫入= 133,099,369讀取= 146,056,197字節/秒 BufferedStreamFile寫入= 131,065,708讀取= 146,217,827字節/秒 BufferedStreamFile寫入= 132694052讀取= 148116004字節/秒

BufferedChannelFile寫入= 186703740讀取= 215075218字節/秒
BufferedChannelFile寫入= 190,591,410讀取= 211,030,680字節/秒BufferedChannelFile寫入= 187,220,038讀取= 223,087,606字節/秒
BufferedChannelFile寫入= 191,585,397讀取= 221,297,747字節/秒 BufferedChannelFile寫入= 192,653,214讀取= 211,789,038字節/秒

MemoryMappedFile寫入= 123,023,322讀取= 231,530,156字節/秒
MemoryMappedFile寫入= 121,961,023讀取= 230,403,600字節/秒
MemoryMappedFile寫入= 123,317,778讀取= 229,899,250字節/秒 MemoryMappedFile寫入= 121,472,738讀取= 231,739,745字節/秒 MemoryMappedFile寫入= 120,362,615讀取= 231,190,382字節/秒

分析

多年來,我一直是直接使用RandomAccessFile的忠實擁護者 ,因為它提供了控制和可預測的執行。 從性能的角度來看,我從來沒有發現使用緩沖流會很有用,而且情況似乎仍然如此。
在最近的測試中,我發現使用NIO FileChannel和ByteBuffer的性能要好得多。 使用Java 7,此編程方法的靈活性已得到改善,可以使用SeekableByteChannel進行隨機訪問。
似乎在某些情況下,讀取RandomAccessFile和NIO可以很好地使Memory Mapped文件贏得寫操作。
我看到這些結果因平臺而異。 文件系統,操作系統,存儲設備和可用內存都會產生重大影響。 在某些情況下,我看到內存映射文件的性能明顯優于其他文件,但這需要在您的平臺上進行測試,因為您的行程可能會有所不同……
推送最大吞吐量時,應特別注意使用內存映射的大文件。 我經常發現操作系統可能由于虛擬內存子系統上的壓力而變得無響應。
結論
從Java執行順序文件IO的不同方法在性能上存在顯著差異。 并非所有方法都遙遙相等。 對于大多數IO,我發現使用ByteBuffers和Channels是IO庫中最優化的部分。 如果緩沖流是您的IO庫的選擇,那么值得進行分支并熟悉Channel和Buffer的實現,甚至可以使用舊的RandomAccessFile進行回退。
參考: Mechanical Sympathy博客上的JCG合作伙伴 Martin Thompson提供的Java順序IO性能 。

翻譯自: https://www.javacodegeeks.com/2012/07/java-sequential-io-performance.html

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/371801.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/371801.shtml
英文地址,請注明出處:http://en.pswp.cn/news/371801.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

text段,data段,bss段,堆和棧

.bss段和.data段的區別 text段,data段,bss段,堆和棧轉載于:https://www.cnblogs.com/jingzhishen/p/6203107.html

[BZOJ3529][Sdoi2014]數表

[BZOJ3529][Sdoi2014]數表 試題描述 有一張Nm的數表&#xff0c;其第i行第j列&#xff08;1 < i < n&#xff0c;1 < j < m&#xff09;的數值為能同時整除i和j的所有自然數之和。給定a&#xff0c;計算數表中不大于a的數之和。輸入 輸入包含多組數據。輸入的第一行…

ZK的實際應用:MVVM –表單綁定

這是我們從頭開始構建ZK應用程序的第二集。 上一篇文章涉及使用MVVM將數據加載和呈現到表中。 在本文中&#xff0c;我們將向您介紹ZK MVVM的表單綁定。 目的 我們將構建一個“添加”功能&#xff0c;使我們能夠將新條目保存到清單中。 單擊“添加”時出現表格 單擊“保存”…

群暉按裝mysql_如何連接群暉里的MYSQL數據庫

匿名用戶1級2018-08-27 回答一、連接遠程數據庫&#xff1a;1、顯示密碼如&#xff1a;MySQL 連接遠程數據庫(192.168.5.116)&#xff0c;端口“3306”&#xff0c;用戶名為“root”&#xff0c;密碼“123456”C:/>mysql -h 192.168.5.116 -P 3306 -u root -p1234562、隱藏密…

ZK的實際應用:MVVM –以編程方式更新視圖

在前兩篇文章中&#xff0c;我們使用ZK的MVVM功能來&#xff1a; 將數據加載到表中 使用表單綁定保存數據 我們已經看到&#xff0c;用注解NotifyChange&#xff08;&#xff09;裝飾方法時&#xff0c;在執行完成后&#xff0c;將向Binder通知VM屬性的更改&#xff0c;以便B…

給你一個笑臉

今日冬至&#xff0c;愿你笑靨如初 <!DOCTYPE html> <html lang"en"> <head> <meta charset"UTF-8" /> <title>Document</title> </head> <body> <canvas id"mycanvas" width"800px&qu…

mysql安裝與配置的截圖_windows下MySQL5.6版本安裝及配置過程附有截圖和詳細說明...

隨著MYSQL版本的更新以及電腦系統的變化&#xff0c;我們給大家整理了各種電腦環境下安裝MYSQL的圖解過程&#xff0c;希望我們整理的內容能夠幫助到大家&#xff1a;mysql安裝圖解總結https://www.jb51.net/article/142398.htm編輯者&#xff1a;Vocabulary下面詳細介紹5.6版本…

mysql 更新日的數據類型_[每日更新-MySQL基礎]5.常用的數據類型-整數和字符串

1. 數據類型在學習PHP的時候我們已經講過數據類型了&#xff0c;所謂數據類型就是數據的格式。每一種數據類型在計算機中存儲的方式會有差異&#xff0c;占用的存儲容量也有區別&#xff0c;所以選擇合適的數據類型可以節約我們的存儲成本&#xff0c;也方便我們的程序運行和…

Hello World with Spring 3 MVC

在2005年&#xff0c;我對Martin Fowler的這篇文章對Spring進行了介紹。從那時起&#xff0c;我就修改了許多IoC框架&#xff0c;包括Guice &#xff0c; PicoContainer &#xff0c; NanoContainer等。雖然我很喜歡與IoC一起工作&#xff0c;但我必須說Spring在過去的5年中&am…

ansible 安裝

1、簡介 ansible是新出現的自動化運維工具&#xff0c;基于Python開發&#xff0c;集合了眾多運維工具&#xff08;puppet、cfengine、chef、func、fabric&#xff09;的優點&#xff0c;實現了批量系統配置、批量程序部署、批量運行命令等功能。ansible是基于模塊工作的&#…

JS原型詳解

參考的別人家的博客http://www.cnblogs.com/ljchow/archive/2010/06/08/1753526.html ###JS原型####JS原型&#xff0c;就是原型對象&#xff0c;簡稱原型。不僅構造函數有&#xff0c;普通函數也有* 普通函數&#xff1a;javascript function puTong(){ }; alert(puTong.proto…

c# mysql 插入 和 查詢_C#對數據庫的操作(增刪改查)

1、【在web.config文件中配置】2、【連接字符串】private static readonly string StrCon ConfigurationManager.ConnectionStrings["sqlConnection"].ToString();3、【查詢數據方法】/// /// 查詢數據/// /// 查詢語句/// 參數/// public static DataTable QueryDa…

利用docker在window7下安裝TensorFlow

安裝過程下碰了不少坑&#xff0c;記錄一下安裝過程&#xff0c;方便以后有需要時復用。 1、安裝docker 下載最新版本的docker并且默認安裝即可&#xff0c;安裝后打開Docker Quickstart Terminal&#xff0c;初次進去需要一段時間。 下載網址&#xff1a;https://www.docker.c…

哈希長度擴展攻擊

在這篇文章中&#xff0c;我將盡力避免夏季的低迷&#xff0c;而將重點放在比抱怨天氣更有趣的事情上-哈希長度擴展攻擊。 散列長度擴展攻擊沒什么復雜或復雜的&#xff0c;說實話&#xff0c;這只是關于如何使用散列函數。 正如我以前的一篇文章中所討論的那樣&#xff0c;哈希…

2017年07月03號課堂筆記

2017年07月03號 星期一 多云 空氣質量&#xff1a;輕度污染~中度污染 內容&#xff1a;MySQL第四節課 in和not in&#xff1b;兩個表的內連接&#xff1b;exists和not exsits的使用&#xff1b;all,any和some&#xff1b; 使用子查詢的注意事項&#xff1b;sql優化&#xff08…

excel文件被寫保護怎么解除_u盤被寫保護怎么解除,看完你就知道了

在平常我們使用U盤存儲資料過程中&#xff0c;有時會發現U盤出現無法正常讀寫的現象&#xff0c;具備表現為U盤被寫保護&#xff0c;無法正常執行讀寫操作。對于小編給大家提供以下解決方法&#xff0c;希望對大家能有所幫助。對U盤執行重置操作01上網搜索并下載“USBOOT”程序…

新建MAVEN項目--pom.xml報錯

使用集成了maven的Eclipse版本新建maven項目后&#xff0c;配置文件pom.xml會在project以及引用的xsd文件處出現錯誤&#xff08;第一、二行報錯&#xff09; 其中一個報錯例子&#xff1a; Multiple annotations found at this line:- Plugin execution not covered by lifecy…

OSGi案例研究:模塊化vert.x

OSGi使Java代碼可以清晰地劃分為多個模塊&#xff0c;這些模塊稱為捆綁軟件 &#xff0c;可以訪問由每個捆綁軟件的類加載器控制的代碼和資源。 OSGi 服務提供了一種附加的分離機制&#xff1a;接口的用戶不需要依賴于實現類&#xff0c;工廠等。 以下案例研究旨在使OSGi捆綁包…

mysql一些常用操作_表的一些常用操作_MySQL

bitsCN.com-創建表(也就是創建表結構)&#xff1a;create table tbl_name(列結構&#xff0c;即有哪些屬性)[表選項]; 如&#xff1a;班級的信息&#xff1a;(班級編號&#xff0c;開班日期)create table java_class(class_num varchar(10),date_start date);注&#xff1a;該表…

網站appache的ab命令壓力測試性能

①&#xff1a;相關不錯的博文鏈接&#xff1a;http://johnnyhg.iteye.com/blog/523818 ②&#xff1a;首先配置好對應的環境上去&#xff0c;有對應的命令 ③&#xff1a;壓力測試的指令如下&#xff1a; 1. 最基本的關心兩個選項 -c -n例&#xff1a; ./ab -c 100 -n 10000 &…