<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    IO性能

    Posted on 2010-01-23 21:03 周舒陽(yáng) 閱讀(2967) 評(píng)論(4)  編輯  收藏
    本期Blog原文參見(jiàn):
    http://www.liferay.com/web/shuyang.zhou/blog/-/blogs/io-performance

          IO操作幾乎對(duì)于所有的應(yīng)用都是非常重要的,因?yàn)镮O操作非常容易導(dǎo)致性能瓶頸。

          在Java的世界里存在兩大類IO,傳統(tǒng)IO(TIO)和新IO(NIO)。外加一個(gè)即將到來(lái)的增強(qiáng)版的NIO——NIO2(JDK7)。
    NIO(以及NIO2)主要用于在一些特定情況下增強(qiáng)性能、提供更好的操作系統(tǒng)層次IO功能集成,但它們無(wú)法完全替代TIO!在許多情況下TIO仍然是你唯一的選擇。

          今天我們就來(lái)討論一下TIO的性能問(wèn)題。

    IO的性能瓶頸主要分為兩類:
    1. 錯(cuò)誤的使用緩沖(buffer)
    2. 過(guò)度的同步保護(hù)

          我們都知道buffer能夠增加IO的性能,但不是每個(gè)人都知道如何正確的使用buffer,在本文結(jié)束時(shí)我會(huì)給出一些最佳實(shí)踐建議。

    第一部分:
          對(duì)于1,錯(cuò)誤的使用緩沖(buffer),存在兩點(diǎn)非常流行的錯(cuò)誤用法和一個(gè)感念上的誤解
    • a)為內(nèi)存IO類(In-memory IO class)添加緩沖(錯(cuò)誤用法)
    • b)為已添加buffer的IO類再次添加buffer(錯(cuò)誤用法)
    • c)Buffer版的IO類和顯式使用buffer間的關(guān)系(概念上的誤解)
          對(duì)于a),這是非常荒謬的!添加buffer的目的是為了將對(duì)IO設(shè)備的多次訪問(wèn)合并為一次訪問(wèn)從而提高性能,In-memory類(如ByteArrayInput/OutputStream)根本就不會(huì)訪問(wèn)任何IO設(shè)備,所以對(duì)它們添加buffer是完全沒(méi)有必要的。
          對(duì)于b),這是完全多余的!你只需要buffer一次就可以了,多于一次的buffer只會(huì)引入更多的棧調(diào)用和垃圾創(chuàng)建。
          對(duì)于c),這需要多一點(diǎn)解釋:
          從本質(zhì)上講,這兩種做法是要達(dá)到相同的目的,但方法不同,這也導(dǎo)致了它們之間具有巨大的性能差異!
    針對(duì)這一問(wèn)題我做了一個(gè)測(cè)試,比較使用Buffer版的IO類和顯式使用buffer在讀/寫文件時(shí)的性能差異。
    下面的測(cè)試結(jié)果顯式了兩者的性能差異:

    讀測(cè)試結(jié)果:(所有數(shù)據(jù)都是在JVM預(yù)熱后取得的,每個(gè)采樣點(diǎn)的時(shí)間是10次讀取操作的總時(shí)間,單位為毫秒)
     文件大小  1K 10K
    100K
    1M
    10M
    100M
    1G
     BufferedInputStream  0  1
     5  53  549  5492  56002
     顯式使用byte[]在FileInputStream上讀取  0  0  1
     10
     113
     1126
     11448




    寫測(cè)試結(jié)果:(所有數(shù)據(jù)都是在JVM預(yù)熱后取得的,每個(gè)采樣點(diǎn)的時(shí)間是10次寫入操作的總時(shí)間,單位為毫秒)
     文件大小  1K 10K
    100K
    1M
    10M
    100M
    1G
     BufferedOutputStream  0  1
     5  45  472  4793  48794
     顯式使用byte[]在FileOuputStream上寫入  0  1  1
     10
     124
     1300
     13138



          為什么會(huì)有如此大的性能差異呢?有兩點(diǎn)原因:
    1. Buffer版的IO類導(dǎo)致很多無(wú)謂的棧調(diào)用(都是裝飾者模式惹得禍decorator pattern)
    2. JDK中所有的Buffer版IO類都是線程安全的,這就意味著它們添加了大量的同步保護(hù)(將在第二部分中詳細(xì)解釋)
          現(xiàn)在你知道了顯式使用buffer要比使用Buffer版的IO類具有更好的性能,所以請(qǐng)盡量多顯式使用buffer,但在兩種特殊情況下你仍然需要Buffer版的IO類:
    • 當(dāng)你在使用第三方庫(kù)時(shí),庫(kù)的api需要IO類作為參數(shù),并且你確定他們內(nèi)部的代碼采用流式方式編碼(非塊式操作),也就是沒(méi)有顯式使用buffer。在不修改他們代碼的前提下,你只能通過(guò)傳入Buffer版的IO類對(duì)象來(lái)提升性能。
    • 另外一種情況就是,如果你很懶,你會(huì)更傾向于使用Buffer版的IO類,因?yàn)樗鼈兡芄?jié)省你幾行代碼(相對(duì)于顯式使用buffer)。
    第二部分:
          對(duì)于2,過(guò)度的同步保護(hù),我指的是JDK的IO包,也就是java.io包。
          我一點(diǎn)也不喜歡這個(gè)包里面的代碼,因?yàn)樗麄兌际蔷€程安全的,也就意味著許多同步保護(hù)。如果我需要同步保護(hù),我會(huì)自己去做,而且我絕不會(huì)去添加任何多余的保護(hù)。但在這一點(diǎn)上JDK的IO包把我逼得無(wú)路可走

          只要你使用JDK的IO包,你就被迫的添加了許多同步保護(hù),即使你完全確定你的代碼運(yùn)行在單一線程的環(huán)境下,你也不能回避這些不必要的保護(hù)。你也許會(huì)好奇的問(wèn),這真的是一個(gè)嚴(yán)重的問(wèn)題嗎?JVM在運(yùn)行時(shí)會(huì)對(duì)弱競(jìng)爭(zhēng)的鎖進(jìn)行優(yōu)化,不是嗎?顯然,它做的優(yōu)化還不到家,讓我們來(lái)看一下性能測(cè)試結(jié)果。

          我翻版了一批JDK IO包中常用的類,這個(gè)翻版完全是API層次的翻版,也就是說(shuō)全部代碼是參照J(rèn)DK IO包的Javadoc寫成, 沒(méi)有直接借用JDK的源碼。因?yàn)镴DK源碼大部分使用GPL協(xié)議發(fā)布,而Liferay的代碼采用MIT協(xié)議發(fā)布,為了不引起IP糾紛只好照葫蘆畫瓢。而實(shí)際上從0開(kāi)始創(chuàng)建這些類一點(diǎn)也不難(僅僅是裝飾者模式而已),只是非常的繁瑣。在我的翻版類中,我移除了全部的同步保護(hù)。而我的測(cè)試也進(jìn)行在單一線程環(huán)境下,所以不用擔(dān)心線程安全的問(wèn)題。

          測(cè)試包含兩部分,第一部分比較原始JDK IO類和我的unsyc版的IO類在讀取內(nèi)存數(shù)據(jù)(In-memory data)時(shí)的性能差異,第二部分比較原始JDK IO類和我的unsyc版的IO類在寫入內(nèi)存數(shù)據(jù)(In-memory data)時(shí)的性能差異。之所以采用內(nèi)存數(shù)據(jù)而不是磁盤數(shù)據(jù)是為了放大同步操作對(duì)整體性能的影響,以便于分析。

    讀測(cè)試結(jié)果:

    寫測(cè)試結(jié)果:

          寫數(shù)據(jù)的測(cè)試曲線不像讀的那樣平滑,原因在于它內(nèi)部使用了一個(gè)動(dòng)態(tài)增長(zhǎng)的byte[],這導(dǎo)致大量GC活動(dòng)(與上一期Blog中我們討論SB時(shí)看到的問(wèn)題相似)。

          好了,現(xiàn)在你應(yīng)該看到了同步保護(hù)是一項(xiàng)多么沉重的操作。我們?nèi)粘i_(kāi)發(fā)中存在大量局限在方法調(diào)用棧內(nèi)的IO類使用,這些情況都是絕對(duì)發(fā)生在單一線程環(huán)境下的。另外一些時(shí)候,即使IO對(duì)象的引用超出了方法調(diào)用棧的作用域,但我們可以通過(guò)分析得知它仍然只會(huì)被單一線程所訪問(wèn),比如web開(kāi)發(fā)中,針對(duì)一個(gè)request的全部處理一般都是由一個(gè)worker thread來(lái)完成的(除非你的后臺(tái)還有其他的異步服務(wù)線程與worker間交換數(shù)據(jù),但這很少見(jiàn))。對(duì)于這樣的情況,你大可以放心的使用這些unsyc的IO類(com.liferay.portal.kernel.io.unsync)。

    更多關(guān)于unsycIO類的信息請(qǐng)查看Liferay的JIRA鏈接:
    http://issues.liferay.com/browse/LPS-6648

          最后給大家留下一些建議:
    1. 如果可能請(qǐng)盡量顯式使用buffer,而不是使用Buffer版的IO類。
    2. 僅在使用第三方類庫(kù)和你很懶的時(shí)候使用Buffer版的IO類。
    3. 當(dāng)你確定你的代碼運(yùn)行在單一線程環(huán)境下,或者你自己添加了同步保護(hù)時(shí),請(qǐng)使用com.liferay.portal.kernel.io.unsync包中的IO類。它們能大幅提高你的應(yīng)用的IO性能。
          這里我提供了一個(gè)消除了對(duì)Liferay其他類文件依賴的com.liferay.portal.kernel.io.unsync包供大家下載使用。不過(guò)還是推薦大家直接學(xué)習(xí)使用Liferay:)
          http://www.tkk7.com/Files/ShuyangZhou/IOPerformance/src.zip

    Feedback

    # re: IO性能  回復(fù)  更多評(píng)論   

    2010-01-24 01:01 by ilovetjf@163.com
    上面的測(cè)試有些不成立
    1 BufferedInputStream的BufferSize沒(méi)設(shè)置好,以致上面結(jié)果。
    2 實(shí)際的磁盤IO不能用內(nèi)存來(lái)模擬,而并發(fā)讀寫有時(shí)候性能來(lái)看低。

    # re: IO性能  回復(fù)  更多評(píng)論   

    2010-01-24 12:24 by libinsong1204@gmail.com
    學(xué)習(xí)了。謝謝
    希望“Master Your ThreadLocals” 也能翻譯成中文版本

    # re: IO性能  回復(fù)  更多評(píng)論   

    2010-01-24 12:44 by 周舒陽(yáng)
    @ilovetjf@163.com
    1)這不是bufferSize的問(wèn)題,測(cè)試中BufferedInputStream的bufferSize和顯式使用的byte[]的大小相同(8k),這個(gè)測(cè)試不是為了找出多大的bufferSize是最佳大小,而是為了比較兩種使用buffer方式的性能差異。
    2)使用內(nèi)存數(shù)據(jù)是為了放大同步保護(hù)對(duì)性能的影響,在實(shí)際應(yīng)用中大量使用內(nèi)存流的例子隨處可見(jiàn)。比如web應(yīng)用中Etag,minifier,gzip,cache等f(wàn)ilter都是要借助于內(nèi)存流來(lái)處理數(shù)據(jù)的,所以內(nèi)存流性能的重要性一點(diǎn)也不必磁盤流的低。

    我沒(méi)明白“而并發(fā)讀寫有時(shí)候性能來(lái)看低”指的是什么?測(cè)試是在單線程下執(zhí)行(文中有說(shuō)明,請(qǐng)仔細(xì)閱讀)。這里想要證明的是synchronized對(duì)單線程的性能影響,之所以強(qiáng)調(diào)這點(diǎn)是因?yàn)镴VM的相關(guān)資料中有說(shuō)明對(duì)于弱競(jìng)爭(zhēng)的鎖JIT會(huì)進(jìn)行優(yōu)化,而實(shí)際測(cè)試結(jié)果顯示優(yōu)化的并不怎么樣。

    # re: IO性能  回復(fù)  更多評(píng)論   

    2010-01-24 12:47 by 周舒陽(yáng)
    @libinsong1204@gmail.com
    請(qǐng)?jiān)徫业臅r(shí)間相對(duì)比較緊張,稍有空閑我會(huì)繼續(xù)翻譯,請(qǐng)稍等一段時(shí)間。以后我也會(huì)爭(zhēng)取在這里首發(fā)一些針對(duì)中國(guó)讀者的內(nèi)容。(不會(huì)讓大家總是比外國(guó)人慢半拍的)

    只有注冊(cè)用戶登錄后才能發(fā)表評(píng)論。


    網(wǎng)站導(dǎo)航:
     

    posts - 3, comments - 15, trackbacks - 0, articles - 0

    Copyright © 周舒陽(yáng)

    主站蜘蛛池模板: 69xx免费观看视频| 国产一级黄片儿免费看| 在线天堂免费观看.WWW| 免费无码又爽又刺激高潮软件 | 亚洲av成人一区二区三区| 无码一区二区三区免费| 亚洲AV无码AV男人的天堂| 日韩精品无码一区二区三区免费| 无码国产精品一区二区免费式直播 | 69式互添免费视频| 亚洲专区先锋影音| 18以下岁毛片在免费播放| 国产免费的野战视频| 亚洲国产精品无码久久久| 最近的中文字幕大全免费版| 亚洲色大网站WWW永久网站| 日本不卡免费新一二三区| 亚洲AV日韩精品久久久久久久| 黄网站免费在线观看| 亚洲春色另类小说| 免费无码又爽又刺激高潮| 久久久久亚洲av无码专区导航| 亚洲免费闲人蜜桃| 亚洲精品久久无码| 亚洲女同成人AⅤ人片在线观看| 在线观看免费黄网站| 亚洲福利视频一区二区三区| 精品免费国产一区二区三区| 一级毛片免费播放男男| 亚洲AV美女一区二区三区| 成年性午夜免费视频网站不卡| 亚洲国产精品网站在线播放 | 亚洲AV无码码潮喷在线观看| 国产精品免费观看视频| 亚洲高清在线播放| 免费无遮挡无码视频网站| 99在线热播精品免费99热| 亚洲 暴爽 AV人人爽日日碰| 久久久久亚洲AV成人网| 免费av欧美国产在钱| 香蕉免费看一区二区三区|