결론 먼저 : 두 가지 읽기 방법의 시간 소모는 거의 동일합니다.
fread를 사용하여 1.4G 바이너리 파일을 읽을 때 약 7초가 걸립니다. 동일한 데이터를 다시 읽을 때 오버헤드 시간은 1초 미만입니다. 따라서 여기에서 메모리 맵으로 읽어보고 fread 방법과 비교하십시오.
1.
fread로 읽기 fread로 바이너리 파일을 읽는 단계는 매우 간단합니다. 파일을 열고 읽으면 됩니다.
다음과 같이 구현됩니다.
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++에서 메모리 매핑 기술 사용