2010-05-12 150 views
9

繼讀出存儲的各種問題和寫入數據流,所有不同的答案定義是這樣的正確的方式做到這一點:爲什麼要使用緩衝區讀取/寫入流

private void CopyStream(Stream input, Stream output) 
{ 
    byte[] buffer = new byte[16 * 1024]; 
    int read; 
    while ((read = input.Read(buffer, 0, buffer.Length)) > 0) 
    { 
     output.Write(buffer, 0, read); 
    } 
} 

兩個問題:

爲什麼要在這些小塊中讀寫?

使用的緩衝區大小的意義是什麼?

回答

6

如果你讀一次一個字節,然後每次調用字節有調用該函數讀取字節的開銷和附加費用(例如,做一個fileposition += 1記住該文件在你,檢查你是否已經到達文件末尾,等等)

如果你讀4000個字節,那麼你有相同的開銷(在上面的例子中,1個函數調用,一個add(fileposition + = 4000) ,並檢查一下你是否在檔案末尾,所以就費用而言,你已經快了4000倍(實際上,還有其他的成本,所以你不會看到那麼大的一個收益,但你已經大幅削減開銷)

當然,您可以創建一個與整個文件一樣大的緩衝區,並獲得絕對最低的開銷。然而:

  • 該文件可能很大 - 比您的程序可用的內存大,所以這隻會失敗。或者它可能太大,你開始使用虛擬內存,這將大大減慢速度。所以把它分成小塊意味着你可以使用一個小的固定大小的緩衝區拷貝數據無限量的

  • 您的操作系統和設備可能能夠同時讀寫數據(例如,從一個物理磁盤驅動器複製到另一個)。如果在寫入所有數據之前讀取所有數據,則必須等待整個讀取過程,然後才能開始寫入。但在很多情況下,你可能可以同時進行兩種操作 - 所以讀一個小塊並開始寫「異步」(在後臺),而你回去讀下一個塊。

  • 您的收益遞減。讀取4個字節而不是1個可能會快4倍。但是閱讀4000,40000或400,000不會加快速度(事實上,由於上​​述原因,較大的緩衝區實際上可能會減慢速度)。在某些情況下,物理設備使用特定的數據大小(例如,每個扇區4096個字節,每個高速緩存行128個字節,或每個數據包1500個字節,或CPU總線上的8個字節(64個位))。將數據劃分成與底層傳輸/存儲機制相匹配(或成倍數)的塊可以幫助硬件更有效地處理數據。

典型的I/O 128KB的4kB的工作之間最適合大多數情況下的緩衝區,你可以調整這些在特定的操作執行,所以有一個適合所有情況的「完美」的大小。

請注意,在大多數I/O情況下,正在使用許多緩衝區。例如從磁盤複製數據時(簡單地說),它從磁盤讀取到硬盤驅動器中的讀取緩存(緩衝區),然後通過接口電纜發送到計算機的驅動器控制器,這也可以緩衝數據。然後它可以通過一個I/O緩衝區傳輸到RAM中,直到你的程序準備好接收它(它可能會在你請求之前提取數據,因爲它期望你繼續讀取數據相同的文件,並試圖緩衝數據,所以你不必等待它)。然後你將它讀入你的緩衝區並寫下來。然後它進入另一個I/O緩衝區,發送到驅動器控制器,傳遞到驅動器,並緩存在寫入緩存中。最終,硬盤驅動器會決定將數據實際存儲在其寫入緩存中,並且您的副本將完成 - 其中大部分發生在後臺,因此在您的程序認爲完成寫入之後的幾秒鐘之內可能無法完成寫入,已經開始另一項任務。 (這就是爲什麼在拔出USB驅動器之前必須「安全地移除」USB驅動器的原因 - 操作系統可能尚未將所有數據實際寫入設備,甚至在計算機表示複製操作完成之後幾秒鐘)

4

您通常可以選擇讀取和寫入的大小。但是,對於特定架構,某些值將更加優化。這些都是,超出我的知識。我一直傾向於堅持我熟悉的fgures,比如4K(在我使用的寫驅動程序的NT系統上的頁面大小)。但是,在用戶模式下進行了大尺寸的實驗,我從未遇到任何問題。我儘量保持IO呼叫的數量儘可能低。

我的建議是,如果塊大小非常小(操作開銷與數量增加)或非常大(IO系統阻塞和飽和),那麼塊大小實際上只是重要的。

我想對於任何特定的情況下,你應該

  1. 最小化IO調用數
  2. 改變這個策略,如果真正性能是一個問題。
相關問題