2012-07-11 91 views
5

我做了一個測試,看看從單個字節數組寫入磁盤上的1GB文件所需的時間與從1024個數組寫入另一個1GB文件(每個1MB)之間是否存在差異。爲什麼寫一個小字節數組到一個文件比寫一個大數組要快?

試寫很多陣列
331.6902毫秒
考試中的寫作大陣
14756.7559毫秒

在這個測試中,「多陣列」實際上是一個byte[1024 * 1024]陣列我寫1024次使用for循環。 「大數組」只是一個填充隨機值的1GB字節數組。

下面的代碼是什麼樣子:

Console.WriteLine("Test Writing many arrays"); 

byte[] data = new byte[1048576]; 

for (int i = 0; i < 1048576; i++) 
    data[i] = (byte)(i % 255); 

FileStream file = new FileStream("test.txt", FileMode.Create); 

sw1.Restart(); 

for (int i = 0; i < 1024; i++) 
    file.Write(data, 0, 1048576); 

file.Close(); 
sw1.Stop(); 
s1 = sw1.Elapsed; 
Console.WriteLine(s1.TotalMilliseconds); 

Console.WriteLine("Test Writing big array"); 


byte[] data2 = new byte[1073741824]; 

for (int i = 0; i < 1073741824; i++) 
     data2[i] = (byte)(i % 255); 

FileStream file2 = new FileStream("test2.txt", FileMode.Create); 

sw1.Restart(); 

file2.Write(data2, 0, 1073741824); 

file2.Close(); 
sw1.Stop(); 

s1 = sw1.Elapsed; 
Console.WriteLine(s1.TotalMilliseconds); 

我包括定時部內的file.Close(),因爲它會調用Flush()法和流寫入到磁盤。

生成的文件尺寸完全相同。

我也許C#可以看到,我總是使用相同的數組,它可能會優化迭代/寫入過程,但結果不是它的2-3倍,它快了大約45倍......爲什麼?

+5

我懷疑這不是寫內存的部分,而是關於導致問題的臨時存儲(即1MB內存),導致硬盤從虛擬內存交換。 – Matthew 2012-07-11 15:02:54

+3

另一件事是緩存命中/未命中:你將有緩存命中1MB的內存,始終保持相同。不過,這不是頁面交換的問題。 – nhahtdh 2012-07-11 15:03:32

+0

這也不是一個公平的比較,是嗎?你沒有關閉小陣列1000次,只有一次。 – Rotem 2012-07-11 15:03:59

回答

5

我認爲造成巨大差異的主要原因是操作系統管理緩存幾乎整個1GB的寫入,你做了小塊。

你需要改變基準的設置方式:代碼應該寫入相同的數據,第一次以1024個塊,第二次寫入一個塊。您還需要通過指定FileOptions.WriteThrough關閉的OS數據的緩存,就像這樣:

var sw1 = new Stopwatch(); 
Console.WriteLine("Test Writing many arrays"); 
var data = new byte[1073741824]; 
for (var i = 0; i < 1073741824; i++) 
    data[i] = (byte)(i % 255); 
var file = new FileStream("c:\\temp\\__test1.txt", FileMode.Create, FileSystemRights.WriteData, FileShare.None, 8, FileOptions.WriteThrough); 
sw1.Restart(); 
for (int i = 0; i < 1024; i++) 
    file.Write(data, i*1024, 1048576); 
file.Close(); 
sw1.Stop(); 
var s1 = sw1.Elapsed; 
Console.WriteLine(s1.TotalMilliseconds); 
Console.WriteLine("Test Writing big array"); 
var file2 = new FileStream("c:\\temp\\__test2.txt", FileMode.Create, FileSystemRights.WriteData, FileShare.None, 8, FileOptions.WriteThrough); 
sw1.Restart(); 
file2.Write(data, 0, 1073741824); 
file2.Close(); 
sw1.Stop(); 
s1 = sw1.Elapsed; 
Console.WriteLine(s1.TotalMilliseconds); 

當你運行這段代碼,結果如下所示:

Test Writing many arrays 
5234.5885 
Test Writing big array 
5032.3626 
1

的原因可能是因爲單個1MB陣列被保存在主內存中,但1GB陣列被換出到磁盤。

因此,在寫入單個數組1024次時,您正在從內存寫入磁盤。如果目標文件是連續的,則在此過程中HDD頭不必移動太遠。

寫入1GB陣列一次,您正在從磁盤讀取內存然後寫入磁盤,很可能導致每次寫入至少有兩個HDD磁頭移動 - 首先從交換文件讀取塊,然後返回到目的地文件來寫它。

+0

我不認爲這是這種情況,因爲我沒有看到太多的資源監視器中讀取,因爲在調試時我可以看到數組被保存在內存中,而不是整個時間移動。 – 2012-07-11 15:51:59

+0

它不會顯示爲「讀取」,它將顯示爲「頁面錯誤」,並且您不會在調試器中看到它。 VMM的工作是讓虛擬內存對程序不可見 - 包括調試器。 – Ben 2012-07-11 16:04:21

+0

好吧,很高興知道!但是我仍然有一個疑問:即使系統當前正在使用或者它在當前程序範圍內,系統是否可以交換變量? – 2012-07-11 16:37:24

0

它可能與操作系統如何處理文件寫入有關。當使用單一的write寫入1GB時,調用OS將不得不暫停寫入許多次以允許其他進程使用磁盤I/O。而且你還沒有緩衝寫入。您可以通過指定較大的bufferSize來優化速度。

public FileStream(
    SafeFileHandle handle, 
    FileAccess access, 
    int bufferSize 
) 
相關問題