<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 周舒陽 閱讀(2968) 評論(4)  編輯  收藏
    本期Blog原文參見:
    http://www.liferay.com/web/shuyang.zhou/blog/-/blogs/io-performance

          IO操作幾乎對于所有的應用都是非常重要的,因為IO操作非常容易導致性能瓶頸。

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

          今天我們就來討論一下TIO的性能問題。

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

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

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

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




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



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

          只要你使用JDK的IO包,你就被迫的添加了許多同步保護,即使你完全確定你的代碼運行在單一線程的環境下,你也不能回避這些不必要的保護。你也許會好奇的問,這真的是一個嚴重的問題嗎?JVM在運行時會對弱競爭的鎖進行優化,不是嗎?顯然,它做的優化還不到家,讓我們來看一下性能測試結果。

          我翻版了一批JDK IO包中常用的類,這個翻版完全是API層次的翻版,也就是說全部代碼是參照JDK IO包的Javadoc寫成, 沒有直接借用JDK的源碼。因為JDK源碼大部分使用GPL協議發布,而Liferay的代碼采用MIT協議發布,為了不引起IP糾紛只好照葫蘆畫瓢。而實際上從0開始創建這些類一點也不難(僅僅是裝飾者模式而已),只是非常的繁瑣。在我的翻版類中,我移除了全部的同步保護。而我的測試也進行在單一線程環境下,所以不用擔心線程安全的問題。

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

    讀測試結果:

    寫測試結果:

          寫數據的測試曲線不像讀的那樣平滑,原因在于它內部使用了一個動態增長的byte[],這導致大量GC活動(與上一期Blog中我們討論SB時看到的問題相似)。

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

    更多關于unsycIO類的信息請查看Liferay的JIRA鏈接:
    http://issues.liferay.com/browse/LPS-6648

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

    Feedback

    # re: IO性能  回復  更多評論   

    2010-01-24 01:01 by ilovetjf@163.com
    上面的測試有些不成立
    1 BufferedInputStream的BufferSize沒設置好,以致上面結果。
    2 實際的磁盤IO不能用內存來模擬,而并發讀寫有時候性能來看低。

    # re: IO性能  回復  更多評論   

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

    # re: IO性能  回復  更多評論   

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

    我沒明白“而并發讀寫有時候性能來看低”指的是什么?測試是在單線程下執行(文中有說明,請仔細閱讀)。這里想要證明的是synchronized對單線程的性能影響,之所以強調這點是因為JVM的相關資料中有說明對于弱競爭的鎖JIT會進行優化,而實際測試結果顯示優化的并不怎么樣。

    # re: IO性能  回復  更多評論   

    2010-01-24 12:47 by 周舒陽
    @libinsong1204@gmail.com
    請原諒我的時間相對比較緊張,稍有空閑我會繼續翻譯,請稍等一段時間。以后我也會爭取在這里首發一些針對中國讀者的內容。(不會讓大家總是比外國人慢半拍的)

    只有注冊用戶登錄后才能發表評論。


    網站導航:
     

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

    Copyright © 周舒陽

    主站蜘蛛池模板: 精品久久久久久无码免费| 亚洲情A成黄在线观看动漫软件| 国产jizzjizz免费看jizz| 蜜臀91精品国产免费观看| 免费国产人做人视频在线观看| 亚洲精品夜夜夜妓女网| 亚洲黄色在线观看网站| 亚洲av综合av一区二区三区| 国产无遮挡色视频免费观看性色| 免费人成视频在线观看不卡| 国产亚洲精彩视频| 人人揉揉香蕉大免费不卡| 女人张腿给男人桶视频免费版| 久久乐国产精品亚洲综合| 亚洲自偷自拍另类图片二区| 亚洲精品视频在线观看免费| 亚洲精品456播放| 亚洲一区二区三区免费视频| 一出一进一爽一粗一大视频免费的| 国产偷伦视频免费观看| 免费观看四虎精品国产永久| a毛片成人免费全部播放| 成人免费a级毛片| 亚洲国产无套无码av电影| 一级做a爰全过程免费视频| 亚洲国产精品一区二区九九| 亚洲中文字幕无码亚洲成A人片| 久久青草免费91观看| 亚洲av永久无码嘿嘿嘿| 全部免费毛片在线| 国产成人精品一区二区三区免费| 亚洲免费一级视频| xx视频在线永久免费观看| 亚洲AV无码日韩AV无码导航| 羞羞视频免费网站日本| 免费羞羞视频网站| 国产一级在线免费观看| 伊人久久大香线蕉亚洲| 国产黄在线观看免费观看不卡| 久久亚洲国产成人精品性色| 日本高清高色视频免费|