Windows文件系统的性能就是要比Linux慢

先说结论,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会更慢,因为是虚拟机)。

开机首轮测试第二次第三次
Windows6868 ms197 ms195 ms
Linux虚拟机248 ms52 ms64 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 <Windows.h>
#include <string.h>
#include <stdio.h>

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 <stdio.h>
#include <time.h>
#include <string.h>
#include <fcntl.h>
#include <dirent.h>

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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值