為什麼使用緩衝區來讀/寫 Streams
在閱讀了有關閱讀和編寫 Streams 的各種問題之後,所有各種答案都將這樣的事情定義為正確的方法:
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); } }兩個問題:
為什麼要在這些較小的塊中讀寫?
使用的緩衝區大小有什麼意義?
如果您一次讀取一個字節,那麼您呼叫的每個字節都有呼叫函式來讀取字節的成本,以及額外的成本(例如,執行 a
fileposition += 1以記住您在文件中的位置,檢查您是否已到達文件結尾,以此類推)如果您讀取 4000 字節,那麼您有相同的成本(在上面的範例中,1 個函式呼叫,1 個添加(文件位置 += 4000),以及一個檢查您是否在文件末尾。所以就間接費用,你只是讓它快了 4000 倍。(實際上,還有其他成本,所以你不會看到那麼大的收益,但你已經大大削減了間接費用)
當然,您可以創建一個與整個文件一樣大的緩衝區,並獲得絕對最低的成本。然而:
- 該文件可能很大 - 比您的程序可用的記憶體大,所以這只會失敗。或者它可能太大以至於您開始使用虛擬記憶體,這將大大減慢速度。因此,將其分成更小的塊意味著您可以通過使用固定大小的小型緩衝區來複製無限量的數據
- 您的作業系統和設備可能能夠同時讀取和寫入數據(例如從一個物理磁碟驅動器複製到另一個)。如果您在寫入所有數據之前讀取了所有數據,那麼您必須等待整個讀取才能開始寫入。但在許多情況下,您可能能夠並行執行這兩個操作 - 因此讀取一小塊並開始“非同步”(在後台)寫入,同時返回並讀取下一個塊。
- 你得到的收益遞減。讀取 4 個字節而不是 1 個字節可能會快 4 倍。但是讀取 4,000、40,000 或 400,000 不會加快速度(事實上,由於上述原因,更大的緩衝區實際上會減慢速度)。
- 在某些情況下,物理設備使用特定的數據大小(例如,每個扇區 4096 字節,每個高速記憶體行 128 字節,或每個數據包 1500 字節,或 CPU 匯流排上的 8 字節(64 位))。將數據分成與底層傳輸/儲存機制匹配(或倍數)的塊可以幫助硬體更有效地處理數據。
通常,4kB 到 128kB 之間的 I/O 緩衝區最適合大多數情況,您可以根據正在執行的特定操作調整這些緩衝區,因此沒有適合所有情況的“完美”大小。
請注意,在大多數 I/O 情況下,會使用許多緩衝區。例如,當從磁碟複製數據時(簡單地說),它從磁碟讀取到硬碟驅動器中的讀取記憶體(緩衝區),然後通過介面電纜發送到電腦的驅動器控制器,該控制器也可以緩衝數據。然後它可能會通過 I/O 緩衝區傳輸到 RAM 中,一直保存到您的程序準備好接收它(它甚至可能在您請求之前獲取數據,因為它希望您繼續從相同的文件,並嘗試緩衝數據,因此您不必等待它)。然後將其讀入緩衝區並寫入。然後它進入另一個 I/O 緩衝區,發送到驅動器控制器,傳遞到驅動器,並記憶體在寫記憶體中。最終,硬碟驅動器將決定將數據實際儲存在其寫入記憶體中,並且您的副本將完成 - 大部分都發生在後台,因此它可能要等到您的程序認為它已經完成寫入後的幾秒鐘後才能完成寫入並且已繼續執行另一項任務。(這就是為什麼您必須在拔下 USB 驅動器之前“安全地移除”它們 - 作業系統可能還沒有真正將所有數據寫入設備,即使在電腦說您的複制操作完成後的幾秒鐘內)