结论先行:两种读取方式的耗时相差无几。
使用fread读取1.4G大小的二进制文件时,大概需要耗时7s。而再次读取同一个数据时,只需要不到1s的时间开销。因此,在此尝试使用内存映射进行读取,并与fread方法进行比较。
1. fread读取
使用fread读取二进制文件的步骤很简单,打开文件并读取就OK了。
其实现如下:
FILE* pFile = fopen("D:\\0TestData\\A.dat", "rb+");
if (pFile == NULL)
{
printf("文件不存在\n");
return ;
}
fseek(pFile, 56, 0);
fread(m_src, file_size, 1, pFile);
fclose(pFile);
2. 内存映射读取
使用内存映射的读取步骤如下:
(1)调用CreateFile函数打开想要映射的文件,得到文件句柄hFile。
(2)调用CreateFileMapping函数,并传入文件句柄hFile,为该文件创建一个内存映射内核对象,得到内存映射文件的句柄hMap。
(3)调用MapViewOfFile函数映射整个文件或一部分到进程的虚拟地址空间。该函数返回文件映射到内存后的起始地址。使用指向这个地址的指针就可以读取文件的内容了。
(4)调用UnmapViewOfFile函数来解除文件映射。
(5)调用CloseHandle函数关闭文件对象,必须传入内存映射文件句柄hMap
(6)调用CloseHandle函数关闭文件对象,必须传入文件句柄hFile。
完整代码实现如下:
#include <windows.h>
#include <iostream>
#include<ctime>
#pragma warning(disable:4996)
void readFile1()
{
size_t file_size = 1513881600;
unsigned short* m_src = (unsigned short*)::malloc(file_size);
//16位无符号型[0,65535]
FILE* pFile = fopen("D:\\0TestData\\A.dat", "rb+");
if (pFile == NULL)
{
printf("文件不存在\n");
return ;
}
fseek(pFile, 56, 0);
fread(m_src, file_size, 1, pFile);
fclose(pFile);
free(m_src);
}
int readFile2()
{
HANDLE hFile = ::CreateFile("D:\\0TestData\\A.dat",
GENERIC_READ,
FILE_SHARE_READ,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL);
if (hFile == INVALID_HANDLE_VALUE)
return 1;
do
{
size_t file_size = 1513881656;
HANDLE map_file = ::CreateFileMapping(hFile,
nullptr,
PAGE_READONLY,
0,
0,
nullptr);
if (map_file == nullptr)
break;
do
{
LPVOID pMap = ::MapViewOfFile(map_file, FILE_MAP_READ, 0, 0, 0);
if (pMap == nullptr)
break;
unsigned short* buf = (unsigned short*)::malloc(file_size);
if (buf)
{
memcpy(buf, pMap, file_size);
//std::cout << buf[2] << std::endl;
::free(buf);
}
::UnmapViewOfFile(pMap);
} while (0);
::CloseHandle(map_file);
} while (0);
::CloseHandle(hFile);
}
int main()
{
clock_t startTime, endTime;
//fread读取
startTime = clock();
readFile1();
endTime = clock();
std::cout << "The total run time1 is: " << (double)(endTime - startTime) / CLOCKS_PER_SEC << "s" << std::endl;
//内存映射读取
startTime = clock();
readFile2();
endTime = clock();
std::cout << "The total run time2 is: " << (double)(endTime - startTime) / CLOCKS_PER_SEC << "s" << std::endl;
system("pause");
return 0;
}
以上程序的运行结果如下:
The total run time1 is: 7.752s
The total run time2 is: 0.936s
看到这个结果开始我以为是内存映射读取速度快。为了更进一步验证,我更换了数据文件,单独运行,发现两种读取方式都相差无几。另外,就是读取同一文件时,读取一次后再次读取时间会大大缩短,这应该是数据被放到缓存中了。对此,在运行了几组数据后,查看了CPU的缓存一直在增加。
参考文献:
[1] 内存映射实现快速读取文件
[2] C++中使用内存映射技术