為什麼 Windows 比 Linux 的檔案搜尋慢很多?
問題是,你在 Linux 下是怎麼“搜尋”的呢。
locate 是預先建立索引的。Mac 下的 Spotlight 也是。
find 的話,也沒比 Windows 快到哪去啊。
你有可供比較的資料嗎?
先說結論,
Windows檔案系統的效能就是要比Linux慢
,這是一個事實。我做過Windows的檔案系統開發(寫檔案系統),也研究過Linux檔案系統,還做過UNIX-like的檔案系統的設計和開發,Windows的檔案系統在各個方面效能都不如Linux。
題主這個問題分兩部分回答:
一、應用軟體搜尋慢
Windows在檔案系統裡搜尋的時候,會搜尋一部分檔案內容,會展開搜尋一部分壓縮包(比如CAB檔案)的內容,並不是只搜尋檔名,所以搜尋負擔更重(要開啟檔案並讀取內容)。而開始選單裡(也就是題主說的左下角)搜尋是基於預先建立的索引,所以更快一些。
這本質上是軟體設計的問題,Windows如果用其它搜尋工具,比如everything等,搜尋速度是比自帶的搜尋要快一些的。但Windows native API仍然比不過Linux,所以才有第二部分。
二、檔案系統訪問慢
為了證明這一點,我專門做了一個實現,分別在Windows和Linux平臺寫兩個最簡單的遞迴列舉目錄的程式碼,Windows使用FindNextFile(),Linux使用readdir(),遞迴列舉一個目錄樹,然後計算時間。其中Windows程式碼是直接執行的,Linux是Ubuntu 20。04
執行在虛擬機器裡
。關閉所有防毒軟體和可能影響效能的軟體。
被測的目標目錄是VirtualBox6。1。32的原始碼包含4萬多個檔案和目錄,總大小1G多,Windows和Linux虛擬機器都在同一個磁碟上,儲存介質速度是一致的(準確的說Linux會更慢,因為是虛擬機器)。
開機首輪測試
第二次
第三次
Windows
6868 ms
197 ms
195 ms
Linux虛擬機器
248 ms
52 ms
64 ms
可以看到不管是無cache(開機首輪測試)還是有cache的情況下,Windows的檔案系統性能都遠不如Linux,所以Windows檔案系統慢是一個事實,而不僅僅只是使用者感覺。
慢的原因有多方面的,可以透過分析Linux程式碼和Windows的原始碼(基於WinXP洩漏的原始碼,還有WRK)來得到結論:
1. Windows的列舉目錄項的操作效率太低。
因為讀目錄每次都要經過一次系統呼叫,所以不管是Windows還是Linux都在設計上儘量減少系統呼叫的次數。
Windows提供的FindNextFileW這個API,每次透過函式NtQueryDirectoryFile最後經系統呼叫到檔案系統驅動,獲得目錄內的子檔案資訊,為了減少系統呼叫次數,NtQueryDirectoryFile一次可以獲得多個子檔案資訊,放到一個快取裡,這個快取的大小是4K:
#define FIND_BUFFER_SIZE 4096
Linux提供的readdir()裡也有類似的動作,Linux的預設快取是BUFSIZ * 4,定義如下(位於glibc):
#define BUFSIZ 8192
也就是說
Linux的預設快取是Windows的8倍大小
,從直觀上就可以看出Linux一次系統呼叫能獲取的子檔案專案更多。
同樣是獲取4萬多個檔案資訊,Windows需要的系統呼叫要比Linux多。系統呼叫對於作業系統來說是一個巨大的開銷,Windows比Linux要慢,大部分原因都是因為系統呼叫太多導致的。
2. Windows目錄資訊比Linux要多。
Windows檔案系統裡有個
短名
的概念,並且Windows預設是UTF-16的編碼
,所以同樣一個英文字串檔名,Linux要比Windows少至少一半的資料
:
Windows:
typedef
struct
_WIN32_FIND_DATAW
{
DWORD
dwFileAttributes
;
FILETIME
ftCreationTime
;
FILETIME
ftLastAccessTime
;
FILETIME
ftLastWriteTime
;
DWORD
nFileSizeHigh
;
DWORD
nFileSizeLow
;
DWORD
dwReserved0
;
DWORD
dwReserved1
;
WCHAR
cFileName
[
MAX_PATH
];
WCHAR
cAlternateFileName
[
14
];
}
WIN32_FIND_DATAW
,
*
PWIN32_FIND_DATAW
,
*
LPWIN32_FIND_DATAW
;
Linux:
struct
dirent
{
#ifdef __USE_FILE_OFFSET64
__ino64_t
d_ino
;
#else
__ino_t
d_ino
;
int
__pad
;
#endif
__off_t
d_off
;
unsigned
short
int
d_reclen
;
unsigned
char
d_type
;
char
d_name
[
256
];
/* We must not include limits。h! */
};
另外,因為Windows一個檔案有兩個名字:長名和短名,所以Windows的name cache比Linux更復雜,需要更多的記憶體,
同一個檔案需要兩個名位元組點
,也間接導致了檔案系統查詢名字的效率更低。
Windows檔案系統內部用的是Unicode,如果用FindNextFileA的話,效能比Unicode版本還慢,因為FindNextFileA呼叫的是FindNextFileW並且還多了一個字符集轉換。
3. Windows快取機制不夠激進
Linux系統啟動以後,一般會先劃分走一半的物理記憶體作為快取,但Windows一直以來都沒有這麼激進的快取策略,所以Linux檔案系統快取效率會比Windows要高,效能也高很多。
4. 其它原因
Windows檔案系統效率低還有很多細節的原因,比如Windows核心的通知機制更長更復雜(尤其表現在刪除檔案效能上)。
Windows安全檢查比較多並且複雜(NTFS許可權比POSIX許可權要多)。
……
所以,上面的幾個共同的原因導致了Windows檔案系統性能比Linux低,操作檔案的效能自然也就比Linux要慢(並且是慢的多了)。
最後,Windows檔案系統訪問慢,是程式碼的問題,不是NTFS的問題,有興趣的同學可以試試在Linux下使用NTFS,你
會發現Linux的NTFS比Windows的還快一些
。
Windows遞迴列舉目錄的程式碼:
#define _CRT_SECURE_NO_WARNINGS
#include
#include
#include
int
TotalFiles
;
int
TotalDirectories
;
void
SearchDirectory
(
LPCTSTR
lpPath
)
{
TCHAR
FindDir
[
MAX_PATH
]
=
{
0
};
TCHAR
SubDir
[
MAX_PATH
]
=
{
0
};
WIN32_FIND_DATA
FindFileData
;
BOOL
bRet
;
wcscpy
(
FindDir
,
lpPath
);
wcscat
(
FindDir
,
L
“
\\
*。*”
);
HANDLE
hFind
=
FindFirstFile
(
FindDir
,
&
FindFileData
);
if
(
INVALID_HANDLE_VALUE
==
hFind
)
{
return
;
}
bRet
=
TRUE
;
while
(
bRet
==
TRUE
)
{
if
(
wcscmp
(
FindFileData
。
cFileName
,
L
“。”
)
!=
0
&&
wcscmp
(
FindFileData
。
cFileName
,
L
“。。”
)
!=
0
)
{
if
(
FindFileData
。
dwFileAttributes
&
FILE_ATTRIBUTE_DIRECTORY
)
{
TotalDirectories
++
;
wcscpy
(
SubDir
,
lpPath
);
wcscat
(
SubDir
,
L
“
\\
”
);
wcscat
(
SubDir
,
FindFileData
。
cFileName
);
SearchDirectory
(
SubDir
);
}
else
{
TotalFiles
++
;
}
}
bRet
=
FindNextFile
(
hFind
,
&
FindFileData
);
}
FindClose
(
hFind
);
}
int
main
(
int
argc
,
char
*
argv
[])
{
LARGE_INTEGER
Freq
,
Start
,
End
;
TCHAR
Path
[
MAX_PATH
];
MultiByteToWideChar
(
CP_ACP
,
0
,
argv
[
1
],
strlen
(
argv
[
1
])
+
1
,
Path
,
sizeof
(
Path
));
QueryPerformanceFrequency
(
&
Freq
);
QueryPerformanceCounter
(
&
Start
);
SearchDirectory
(
Path
);
QueryPerformanceCounter
(
&
End
);
printf
(
“Total Files [%d] Total Directories [%d]
\n
”
,
TotalFiles
,
TotalDirectories
);
printf
(
“Counter [%lld] Freq [%lld], Total [%lld] ms
\n
”
,
End
。
QuadPart
-
Start
。
QuadPart
,
Freq
。
QuadPart
,
(
End
。
QuadPart
-
Start
。
QuadPart
)
*
1000
/
Freq
。
QuadPart
);
return
0
;
}
Linux列舉子目錄的程式碼:
#include
#include
#include
#include
#include
int
TotalFiles
;
int
TotalDirectories
;
int
SearchDirectory
(
char
*
path
)
{
DIR
*
dirfd
;
struct
dirent
*
entry
;
char
subpath
[
256
];
dirfd
=
opendir
(
path
);
while
((
entry
=
readdir
(
dirfd
))
!=
NULL
)
{
strcpy
(
subpath
,
path
);
strcat
(
subpath
,
“/”
);
strcat
(
subpath
,
entry
->
d_name
);
if
(
strcmp
(
entry
->
d_name
,
“。”
)
!=
0
&&
strcmp
(
entry
->
d_name
,
“。。”
)
!=
0
)
{
if
(
entry
->
d_type
==
DT_DIR
)
{
TotalDirectories
++
;
SearchDirectory
(
subpath
);
}
else
{
TotalFiles
++
;
}
}
}
closedir
(
dirfd
);
return
0
;
}
int
main
(
int
argc
,
char
*
argv
[])
{
time_t
st
,
ed
;
st
=
clock
();
SearchDirectory
(
argv
[
1
]);
ed
=
clock
();
printf
(
“Total Files [%d] Total Directories [%d]
\n
”
,
TotalFiles
,
TotalDirectories
);
printf
(
“Counter [%ld] Freq [%ld], Total [%ld] ms
\n
”
,
ed
-
st
,
CLOCKS_PER_SEC
,
(
ed
-
st
)
*
1000
/
CLOCKS_PER_SEC
);
return
0
;
}
輸出
你可以使用everything搜尋,全盤建立索引只需3秒,全盤搜尋0。01秒出結果,即輸即所得。
voidtools
這個軟體的速度,絕對遠超linux的檔案搜尋。
硬體佔用極低
,硬碟讀寫小於0。1M/s,CPU佔用小於0。1%,同時一秒鐘可以索引100萬檔案,支援百萬檔案的毫秒級排序,支援顯示資料夾大小。
因為windows搜尋檔案,預設是會檢索檔案內容的,這是最耗費時間的。
如果你不檢索檔案內容,可以非常快,例用系統的api,只要檔名,百萬條檔案只要3-5秒左右,這還是機械硬碟。固態硬碟更快。
如果你直接檢索檔案分配表,可以更快。
主要的區別就是windows搜尋檔案,預設會檢索檔案內容。
慢?
我還在為Linux下沒有Everything這樣的工具苦惱呢