首页
社区
课程
招聘
[讨论]ReadFile能跑满速,fopen+fread测试速度都不行。有解不?
发表于: 2020-12-19 17:12 5321

[讨论]ReadFile能跑满速,fopen+fread测试速度都不行。有解不?

2020-12-19 17:12
5321

测试环境:

3个970Pro SSD组RAID后,用多种工具测试,读的速度都在7.5GB/秒左右,写的速度在6GB/秒左右。而且写的是随机内容,非0,共测试50G大小可排除缓存。

发现自己的程序在读取时性能不行,我就单独写了个测试demo,结果如下:

代码如下:

void Test1(bool bWriteTest, char* pBlock, wchar_t *pFile, ULONGLONG llFileSize, ULONGLONG llTotalTestByte, ULONGLONG llBlockByte)
{
	const wchar_t* pFileOpt = L"rb";
	if (bWriteTest)
		pFileOpt = L"wb";

	FILE* hFile = _wfopen(pFile, pFileOpt);
	if (hFile == 0)
	{
		printf("待测试文件打开失败!\n");
	}
	else
	{
		setvbuf(hFile, NULL, _IONBF, 0);
		setbuf(hFile, 0);

		ULONGLONG llTotalTestedLen = 0;
		ULONGLONG llLastTestedLen = 0;
		DWORD t1 = timeGetTime();
		while (llTotalTestedLen < llTotalTestByte)
		{
			if (bWriteTest)
			{
				size_t iWriteLen = fwrite(pBlock, 1, llBlockByte, hFile);
				if (iWriteLen != iWriteLen)
				{
					printf("测试操作出错!\n");
					break;
				}
				llTotalTestedLen += iWriteLen;
			}
			else
			{
				size_t iReadLen = fread(pBlock, 1, llBlockByte, hFile);
				if (iReadLen != llBlockByte)
				{
					if (feof(hFile))
						fseek(hFile, 0, SEEK_SET);
				}
				llTotalTestedLen += iReadLen;
			}

			DWORD t2 = timeGetTime();
			DWORD t = t2 - t1;
			if (t > 1000)
			{
				t1 = t2;

				ULONGLONG llTotalTestedMB = llTotalTestedLen / 1024 / 1024;
				ULONGLONG llCurrentTestedMB = (llTotalTestedLen - llLastTestedLen) / 1024 / 1024;
				ULONGLONG llTestSpeed = llCurrentTestedMB * 1000 / t;
				llLastTestedLen = llTotalTestedLen;
				printf("Total %lld MB, %lld MB/s\r", llTotalTestedMB, llTestSpeed);
			}
		}
		fclose(hFile);
	}
}

我一直认为,读写大块速度才最高,因为我们最常见的复制大文件远比复制小文件速度快很多。而且大块可能在磁盘上连续的概率要高得多。

基于已有的20G文件的测试结果:

每次读写100MB时,读写速度才1.5GB/秒

每次读写10MB时,读写速度提高到3GB/秒

每次读写1MB时,读写速度提高到3.3GB/


这与我的预测完全不一样,越小的块速度越快。

另外,网上说CreateFileMapping方式会快很多,我又写了一套基于CreateFileMapping的函数,测试结果跟fread差别不大,代码就不列了。


又基于_open _read _write写了一套,效果基本跟fopen差不多。


又基于CreateFile,ReadFile,WriteFile写了一套,常规使用方法,效果基本跟fopen差不多。

后来又仔细阅读CreateFile相关API,发现加上FILE_FLAG_NO_BUFFERING和FILE_FLAG_SEQUENTIAL_SCAN,在读写时速度就能跑到满速了


但是:我写的是播放器,解码部分用的ffmpeg,还有其它一些第三方库,在播放某些8K级别素材时,需要极限的读取速度,我不大可能把第三方库全改成windows api调用。

请问:是否有基于fopen open等 c函数的代码,通过某些参数设置,让整个程序能跑满速?


另外:

我用procexplorer查看HDTune,在测试时线程显示就是 exe直接调用Ntdll.dll的zwReadFile。

为何我自己基于API写的程序,是我的ReadFile先调用到 KernelBase.dll的ReadFile,再调用到Ntdll.dll的zwReadFile啊?

HDTune不应该直接调用Kernel.dll的ReadFile吗?



[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!

收藏
免费 0
支持
分享
最新回复 (7)
雪    币: 4519
活跃值: (5144)
能力值: ( LV4,RANK:40 )
在线值:
发帖
回帖
粉丝
2

有点意思,设置下c文件API的缓存大小试试,实在不行的话,HOOK API吧,发现是你的目标文件的句柄,就以你说的FILE_FLAG_NO_BUFFERING和FILE_FLAG_SEQUENTIAL_SCAN参数重定向调用到win32的文件API。

最后于 2020-12-19 17:58 被sonyps编辑 ,原因:
2020-12-19 17:55
0
雪    币: 4519
活跃值: (5144)
能力值: ( LV4,RANK:40 )
在线值:
发帖
回帖
粉丝
3

HANDLE (WINAPI* Real_CreateFileW)(
  __in      LPCWSTR lpFileName,
  __in      DWORD dwDesiredAccess,
  __in      DWORD dwShareMode,
  __in_opt  LPSECURITY_ATTRIBUTES lpSecurityAttributes,
  __in      DWORD dwCreationDisposition,
  __in      DWORD dwFlagsAndAttributes,
  __in_opt  HANDLE hTemplateFile
)=CreateFile;
HANDLE WINAPI My_CreateFileW(
  __in      LPCWSTR lpFileName,
  __in      DWORD dwDesiredAccess,
  __in      DWORD dwShareMode,
  __in_opt  LPSECURITY_ATTRIBUTES lpSecurityAttributes,
  __in      DWORD dwCreationDisposition,
  __in      DWORD dwFlagsAndAttributes,
  __in_opt  HANDLE hTemplateFile
)
{
if(lpFileName==你关注的文件)
{
   dwFlagsAndAttributes|=FILE_FLAG_NO_BUFFERING|FILE_FLAG_SEQUENTIAL_SCAN;
}
return Real_CreateFileW(lpFileName,dwDesiredAccess,dwShareMode,lpSecurityAttributes,dwCreationDisposition,dwFlagsAndAttributes,hTemplateFile);
}

最后于 2020-12-19 18:17 被sonyps编辑 ,原因:
2020-12-19 18:15
0
雪    币: 174
活跃值: (620)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
4
sonyps 有点意思,设置下c文件API的缓存大小试试,实在不行的话,HOOK API吧,发现是你的目标文件的句柄,就以你说的FILE_FLAG_NO_BUFFERING和FILE_FLAG_SEQUENTIAL ...
我更想搞明白fopen是否有满速的写法 :)
hook会影响整个程序的CreateFile,程序里乱七八糟的太多,怕影响其它业务,暂时先不这么搞。
2020-12-21 09:17
0
雪    币: 15
活跃值: (393)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
5
sunbinjin 我更想搞明白fopen是否有满速的写法 :) hook会影响整个程序的CreateFile,程序里乱七八糟的太多,怕影响其它业务,暂时先不这么搞。
if(lpFileName==你关注的文件)
{
   dwFlagsAndAttributes|=FILE_FLAG_NO_BUFFERING|FILE_FLAG_SEQUENTIAL_SCAN;
}
判断了还怕啥??
2020-12-25 08:43
0
雪    币: 174
活跃值: (620)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
6
vaguem if(lpFileName==你关注的文件) { dwFlagsAndAttributes|=FILE_FLAG_NO_BUFFERING|FILE_FLAG_SEQUENTIAL_SCAN ...
有问题的,因为这个微软文档里提出来,读写要求严格和分区的簇信息对齐,不能随意读写,会失败的。
感觉这个会很麻烦,对于那些随便读取的方式,理论上得先读大块,再拆出想要的快。
2020-12-25 12:55
0
雪    币: 1319
活跃值: (1960)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
7
sunbinjin 有问题的,因为这个微软文档里提出来,读写要求严格和分区的簇信息对齐,不能随意读写,会失败的。 感觉这个会很麻烦,对于那些随便读取的方式,理论上得先读大块,再拆出想要的快。

楼上正解,文件读写最主要还是以块进行读取得,得从基于这个方面去考虑优化。 windows得话大文件最好以CreateFileMapping这种去处理。

最后于 2020-12-25 13:21 被库尔编辑 ,原因:
2020-12-25 13:18
0
雪    币: 12848
活跃值: (9147)
能力值: ( LV9,RANK:280 )
在线值:
发帖
回帖
粉丝
8

你这个需求直接重载crt函数是最简单的了吧

最后于 2020-12-25 19:12 被hzqst编辑 ,原因:
2020-12-25 19:11
0
游客
登录 | 注册 方可回帖
返回
//