為什麼單個大文件比總體積相同的多個小文件複製起來要快很多?

比如說,在我的電腦上(windows操作系統,NTFS文件系統),複製一個1G的文件到磁碟的另一個分區所花的時間比複製1024個1M的文件到磁碟的另一個分區所用的總時間要小很多。


各位都把這個問題描述的太複雜了吧。

複製一個文件需要做的有1-3步:

1、在目標位置創建對應的文件名項,因為文件名也是要保存在磁碟上的;

2、如果文件有內容,把文件內容寫入磁碟,並按照塊對齊(512位元組-64K不等)

3、如果文件有內容,在一個特定的位置把文件內容的塊的信息記錄下來,標記這些塊屬於這個文件並且是被使用了。

因為題主問的是1M的文件,所以這三步都是需要的。

對於1G的文件,需要的也是這三步,對於1024個1M的文件,需要的是1024×3步。

對於磁碟設備有IOps的概念,就是每秒能執行的I/O次數,對於複製1024個1M文件來說,那麼至少需要1024*3次I/O,對於1G的文件來說,至少需要3次。所以從次數來說,複製小文件越多,磁碟讀寫次數越多,雖然有cache等一系列優化的機制,但整體次數還是要高很多的。

並且,磁碟寫1位元組,和寫512位元組(一個扇區)的代價是相同的,雖然寫入文件名短,但仍然需要寫入512位元組(一個扇區)。

因此寫1G文件就至少要比寫1024*1M文件多寫512K這麼多數據。

如果說512K這麼多數據好像也不太多,但還有一個不可忽略的操作就是比較文件名:

複製文件的時候,需要判斷是否有重名,複製1個文件,檢查1次就可以了,複製1024個文件,就需要檢查1024次,並且隨著文件的增多,檢查的負擔也越重(要跟之前的文件都檢查一下)。

所以總結下來有三點:

1、小文件導致IO次數增多,磁碟IO次數本身就有瓶頸;

2、小文件實際寫入的內容也更多(這裡我沒包括索引項,實際索引項也很多,但不好表達);

3、小文件導致CPU負擔更重,需要匹配更多的信息。

以上三點造成了小文件複製比大文件慢,所有操作系統的所有文件系統基本上都有以上三個原因。

如果題主有興趣,可以去看Linux源碼,Windows的源碼下載一個WINDDK可以看到FAT驅動源碼。


主要是元數據操作的耗時。例如,讀取每個文件都要先讀取對應inode,這是一次磁碟IO。

===============

鑒於這是我在知乎獲得的最多贊同的答案(儘管才3個贊同,乃們不要笑我)。我決定再整理補充一點。

inode的相關知識可以看阮一峰的這篇博客(理解inode - 阮一峰的網路日誌)。

我簡單重述一下:

磁碟以扇區為單位,所以存在磁碟上的文件具體在那些扇區上需要記錄下來,這個信息就記錄在這個文件對應的inode中。當然,inode本身也要存放在磁碟上。所以當讀取一個文件的時候,首先要讀取到這個文件inode(這是一次磁碟IO),從中得知文件內容在哪些扇區中,然後讀取這些扇區的內容。

所以,泛泛而談的話(更精細的原因不好確定,正如劉賀所說),時間差距應該主要來源於inode操作。

ps.

1. 劉賀提到的幾個因素都很正確且有啟發性。另外,劉賀在評論中的解釋更詳細,移至答案中更合適,建議題主看看。已感謝! @劉賀

2. 我是之前學習小文件存儲的時候了解到這個問題的,在Facebook
Haystack那篇論文的Background中提到了類似的問題。希望對題主有幫助。


其實這個事情,簡單的說,就是 Windows 的文件系統對小文件的優化不到位。原因可能有很多,大家很多人都已經說了,我就不重複。

我理解劉賀的意思。不過其實大家也都理解題主說的一定是 Windows。

ext4 在這個問題上確實有明顯優勢,但要認真去研究這個問題其實很難的,原因是:

在 Linux 這樣的我們能看到源代碼並進行分析的系統中,其實這個現象並不特別明顯。從純學術的角度來看,無法推斷問題發生的原因。

在 Windows 系統中雖然很容易重現這個現象,但我們無法分析其原因,因為沒有源代碼。

所以這個問題的真正答案,恐怕只有微軟的內部人員可以給出。


因為複製文件的時候所作的事情不僅僅是複製數據,一般還有一個類似於初始化的過程,而這個初始化的過程所需的時間又和文件大小沒什麼關係,所以,當你複製很多小文件的時候,這個初始化過程所需的總時間可能會很多,多到和複製數據所需的時間差不多,這時你就覺得異常了。

由於不同的文件系統的這個初始化過程的細節是不一樣的,所以這裡不討論細節。下面打個比方吧。

小學老師布置了一份作業,抄寫某課文10次。一般的同學都會找來作業本,削好鉛筆,打開課本找到那篇課文,開始抄寫,直到抄完10次。但是,有一個比較笨的同學,一開始他的做法和大家一樣,但是每抄完一次,他就重新把作業本、鉛筆和課本放回原處,然後,重新拿來作業本,削鉛筆,打開課本找到課文,開始抄寫。這個比較笨的同學就是讓你感覺複製小文件很慢的那種文件系統。


這個問題太模糊了。影響文件系統性能的因素有很多:

  • 是從哪兒複製到哪兒?
  • 都是存在什麼介質上的?介質的性能是什麼樣的?
  • 都是什麼文件系統?什麼操作系統?
  • 文件系統有配緩存嗎?緩存有多大?複製的時候是從緩存讀的還是從硬碟讀的?
  • 1024個小文件是一起連續產生的嗎?
  • 1024個小文件的複製過程是串列的還是並行的?

但除去這些細節不說,一個很籠統的回答:產生1024個1M的文件比產生一個1G的文件需要多產生1023個文件名。而一個不太優化性能的文件系統可能會因此花掉很多額外的時間。

如果您比較一下複製一個1T的文件的時間和複製1024個1G的文件的時間,可能時間差別就不會那麼大了。

--

好吧好吧,多解釋幾句:問題本身問得是很清楚的。但是這個問題里描述的實驗現象和實驗條件關係很大。在不同的實驗條件下會有完全不同的實驗結果。問題中對具體實驗條件描述得很模糊,除了在舉例中給出了文件大小,其他什麼都沒說。因此很難對實驗現象作出準確的解釋。

如果說問題問得有什麼問題,那麼最大的問題是沒有先問「是不是」,就直接問了「為什麼」。

--

(應要求把評論里的東西移上來。我覺得這段才算有點不講人話……)

技術方面,就說說機械硬碟吧。這東西的讀寫性能是很隨機的一個事情,除了和是否是順序讀寫有關之外,和尋道有關係,和存在里圈還是外圈也有關係,實際表現往往呈現很大的隨機性。不信可以自己寫個腳本測一測讀寫文件的速度,看在文件系統層能不能把讀寫時間浮動控制得很小。我當初上課做硬碟性能測量的時候都是要固定讀寫硬碟的某個物理位置才能得到比較穩定的數據。

所以,問題里的這個現象,如果不把條件說清楚,別說解釋為什麼,連重複都很難。

比如,我自己在Linux Ext4的機械硬碟上就無法重複這個現象。我在自己的home目錄下拷貝1G文件的時間總是比1024個1M文件要慢若干秒。為什麼會這樣呢?可能的因素很多很複雜。最大的原因可能是因為Ext4是存Journal的,所以1024個小文件的很大部分還都在Journal里操作,離得很近,尋道很快,甚至是在同一個block里。而那個1G的文件卻不會全存在Journal里,所以要反覆在不同的位置讀寫硬碟,所以相對慢一點。

如果是在Windows的FAT32下恐怕就是另一回事了,因為Journal沒了。

如果複製的源位置和目標位置是兩塊不同的硬碟又是另一回事了,因為現在讀寫是獨立的兩個尋道了。

如果硬碟不是機械硬碟而是SSD就又是另一回事了,因為尋道沒了。

--

說下樓下某個SSD測量的結果。我不知道那個工具具體測的是什麼,但猜度起來,那個主要測量的性能差距應該是順序讀寫和隨機讀寫的區別。原因是因為各種介質(內存,硬碟,匯流排,等等……)上順序讀寫都比隨機讀寫要快(因為定址常常是個力氣活)。但這和大文件和若干小文件是不一樣的。大文件不一定是順序存的。一大堆小文件也不一定隨機存的。


一百畝麥田,一台收割機。
1、這一百畝地分屬於一百戶農民,每家一畝散落在村子四周,收割時你需要完成一家再接下一家;
2、這一百畝地連片屬於一戶,收割時一氣兒到底 。
————————
農民出身的我就靠種地的經驗學習和理解計算機知識的。


@劉賀


盜個圖
我簡化一下問題,這個一個固態硬碟的文件讀寫測試,試分析各項目速度差距產生的原因。


文件系統開銷(文件名,各種許可權, 創建/修改/訪問時間,塊分配...) + 磁碟的整塊讀寫(即使 文件只有一個位元組,也要存取佔用整個塊,4K)


並非一定快。 但單的大文件複製速度快的概率大。
假設為無故障機械硬碟。
硬碟操作兩種文件速度比較。
1.單個大文件在硬碟上物理地址連續分布的概率比多個小文件在硬碟上物理地址連續分布的概率大。
對於機械硬碟,連續分布地址上讀取速度較快。

假如在有10個房間各有一個球,按照1、2、3…10的順序取球比1、3、5、2、4、8…這樣的順序取球快。

2.第二個原因是我猜的,揣測一下,複製的時候要不要處理數據呢?假如每個文件都有加密/壓縮/校驗的話,也許大文件只需一次演算法,小文件要進行1024種演算法。
當然,這是一部分原因,還有其他的呢~涉及的流程太長了。


用過FTP的都知道。。複製一個執行一次命令,複製多個執行多個命令。


沒人來回答一下rsync為啥那麼快?


其實體積不同的小文件複製也很慢


一個是元數據的overhead

另外大文件更可能是連續存放的


上層來說,影響性能的通常是io。然後是cache。順序讀寫對cache有利。

假設你是操作系統開發者,設身處地去想就很容易理解了。比如你知道讀硬碟時尋道是很慢的,所以你的讀文件操作的實現是預先往前讀多比如幾百k(雖然用戶指定的是幾十k),到內存或者硬體(硬碟自帶的)cache,如果剛好用戶也是想讀下幾百k,那這時你從內存提交到用戶,不用硬碟的機械設施去定址讀入,顯然速度就高了……這只是通俗不嚴謹的例子……(當然小文件假定隨機的情況下,他們不是連續存儲的;驅動/操作系統或運行時/api的開發者也是基於同樣假設去優化;非要針對連續存儲的n個小文件來實現系統,會比同樣總大小的一個文件慢嗎?當然可以有一樣快的實現方法!)

這裡面其實有很多層抽象層次和策略,要對體系結構和操作系統有一定思想性的了解才有體會。但再多優化也不外如是基本原理:
分層體系,cache和預測策略。

舉一反三地說,所有硬體訪問(存儲io,圖形硬體io,甚至cpu指令流水等優化機制)都基於這種基本思想。

這不是一個好答案,你們體會一下。


簡單說 window的話會產生碎片,什麼是碎片呢。

就是硬碟block的位置,簡單講就是硬碟存儲的最基本單位塊,window是沒有inode的概念。

一個大文件分別存儲在N個不同存儲塊中 block1 block2 block3 .... ,一個文件的起始位置在block1中 他可能在第一塊硬碟的第2個扇面, 然後他存儲數據的同時,也保留下一個block2在硬碟的位置索引,當讀完block1的位置之後,按索引找block2。以後以此類推 block2讀完 再找block3,線性查找直到最後一個block讀完。

那麼 一個問題出現了,block在硬碟中的位置不是排排坐的,很多時候都是天南海北的。磁頭一次尋碟不一定找的。這種分崩離析的狀態就是磁碟碎片,整理磁碟碎片就是讓屬於一個文件的block在硬碟中的位置排排坐,便於快速定址,所以磁碟清理 之後就顯得電腦快多了。

一個大文件都如此,那麼N個小文件豈不更加「分崩離析」,自然不肯老老實實的排排坐,即便是磁碟整理,因為是不同的文件,你也奈他不何。

所以傳統的機械硬碟就是給人這樣的趕腳。

固態硬碟呢

雖然也是這個window硬碟定址的道理沒有變。

但是 但是 但是

人家固態根本不是用磁頭,碟盤轉圈來找,而是內存定址一樣,不管天南與海北。速度soso的。

所以固態硬碟不存在磁碟碎片。

大概是我的理解,可能有誤。


在Win上據說每複製一個文件都會校驗一下,而很多小文件校驗花的時間就會比較大了。

題主可以測試一下,把小文件利用好壓的「存儲」方式來把小文件直接打包拷貝(不是先打包,而是直接把包直接存到目的位置),你會發現這樣速度比較快。


推薦閱讀:

TAG:操作系統 | 文件系統 | 計算機科學 | 計算機專業 |