【问题标题】:To read a file,which one will be faster?using fstream,FILE* or memory mapping?要读取文件,哪个会更快?使用 fstream、FILE* 还是内存映射?
【发布时间】:2016-03-10 02:20:21
【问题描述】:

这里是我的项目配置:vs2013,win32,Debug。

我想知道对于不同的文件大小,3种文件读取方式哪个会更快。它们是c++风格的fstream,c风格的文件读写和内存映射。

但执行后,这是我的结果:

文件大小 1225284
fstream 时间 47
c文件指针时间0
内存映射时间0

文件大小 14856192
fstream 时间 15
c文件指针时间0
内存映射时间 47

文件大小 97198080
fstream 时间 16
c文件指针时间0
内存映射时间 265

文件大小 1259530844
fstream 时间 31
c文件指针时间16
内存映射时间 11138

似乎对于流和FILE*读取,读取文件所需的时间不会随着文件大小的增加而增加。但是对于内存映射,这是真的。这种现象很奇怪。
因为在我看来,对于大文件,内存映射会更快。 这是我的代码:

string ifile = "M:/Thesis/FileReadCmp/1.txt";
string os = "M:/Thesis/FileReadCmp/new_cmp1.txt";
int page_size = 2 * 1024 * 64 * 1024;//128M
for (int j = 0; j < 100; ++j){
    os[os.size() - 5] = '1' + j;
    ofstream o(os);
    for (int i = 0; i < 4; ++i){
        ifile[ifile.size() - 5] = '1' + i;
        ifstream in(ifile);

        in.seekg(0, ios::end);
        o << "File Size " << in.tellg() << endl;
        o << endl;
        in.close();

        //using fstream to read file
        long long st = GetTickCount();
        in.open(ifile);
        char c;
        while (in >> c){
            ;
        }
        in.close();             
        long long et = GetTickCount();
        o << "fstream time " << et - st << endl;

        //using FILE* to read file
        st = GetTickCount();
        FILE* cpf = fopen(ifile.c_str(), "r");
        char cc = fgetc(cpf);
        while (cc != EOF)
        {
            cc = fgetc(cpf);
        }
        fclose(cpf);
        et = GetTickCount();
        o << "c file pointer time " << et - st << endl;

        //using memory mapping to read file
        const char* pc = ifile.c_str();
        st = GetTickCount();
        HANDLE hFile = CreateFile(pc, GENERIC_WRITE | GENERIC_READ, 0,
            NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
        int file_size = GetFileSize(hFile, NULL);
        HANDLE hFileMap = OpenFileMapping(FILE_MAP_READ | FILE_MAP_WRITE, FALSE,
            TEXT("SharedData"));

        if (hFileMap == NULL){
            // if no such object,create a file mapping object
            hFileMap = CreateFileMapping(hFile, NULL, PAGE_READWRITE,
                0, 0, TEXT("SharedData"));
        }

            int rem_file_size = file_size;
            int offset = 0;
            while (rem_file_size > page_size){
                PVOID pvFileView = MapViewOfFile(hFileMap, FILE_MAP_WRITE, 0, offset, page_size);
                char* asc_dex = (char*)pvFileView;
                for (int i = 0; i < page_size; ++i){//, c = 0++c
                    char c = asc_dex[i];
                }
                //UnmapViewOfFile(pvFileView);
                offset += page_size;
                rem_file_size -= page_size;
            }
            PVOID pvFileView = MapViewOfFile(hFileMap, FILE_MAP_WRITE, 0, offset, rem_file_size);
            char* asc_dex = (char*)pvFileView;
            for (int i = 0; i < rem_file_size; ++i){//, c = 0++c
                char c = asc_dex[i];
            }
            UnmapViewOfFile(pvFileView);

        CloseHandle(hFileMap);
        CloseHandle(hFile);

        et = GetTickCount();
        o << "memory mapping time " << et - st << endl;
        o << endl;
    }
}

【问题讨论】:

  • 为什么要单独映射每个页面?
  • 因为1GB大的文件,我无法将文件作为一个整体进行映射。最让我困惑的是使用stream和FILE*@immibis的读取速度
  • 我已经删除了 UnmapViewOfFile(pvFileView);在循环内。@immibis
  • 哎呀,我没有注意到你有 128MB 的页面 - 我假设 page_size 是页面的大小(只有 4kB)。对于 128MB 的“页面”,映射每个页面的开销应该不是问题。
  • 我认为UnmapViewOfFile 应该在循环内;否则就是泄漏,不是吗?

标签: c++ file stream memory-mapping


【解决方案1】:

最有效的方法是保持输入流的流动。通常,这意味着读取大块数据(每个请求更多数据)。

一种方法是拥有多个缓冲区(在互联网上搜索“双缓冲”)和多个线程。

读取线程将读取并填满一个缓冲区,然后发出数据准备就绪的信号。读取线程继续填充下一个缓冲区。

处理线程(正在等待读取信号)唤醒并开始处理缓冲区,然后输出数据。

一个目标是使用尽可能多的大缓冲区来调整读取数据的速度与处理速度。同样,这个想法是数据的读取在处理方面是连续的。

【讨论】:

  • 你的建议很好。但是对于3种文件读/写方法?对于不同类型的文件,如果我不使用你上面提到的方法,哪一种会更好?@Thomas Matthews
  • 从文件中读取数据时,文件类型无关紧要。您想输入数据然后分析(解析)数据。打开和读取数据可能比使用内存映射更快。内存映射取决于操作系统,操作系统可能需要执行上下文切换或分页以适应请求。当您自己读取文件时,您可以管理读取时间和分配的内存量。
【解决方案2】:

问题可能是开放模式。内存映射的默认开放模式是二进制。

当我把in.open(ifile);改成in.open(ifile, ios::binary);FILE* cpf = fopen(ifile.c_str(), "r");改成FILE* cpf = fopen(ifile.c_str(), "rb");时,3个方法的实际运行时间就变成了理论。分别如下:

文件大小 1225284
fstream 时间 297
c文件指针时间93
内存映射时间0

文件大小 14856192
fstream 时间 3167
c文件指针时间1045
内存映射时间 47

文件大小 97198080
fstream 时间 20779
c文件指针时间6833
内存映射时间 281

文件大小 1259530844
fstream 时间 268696
c文件指针时间88406
内存映射时间 11216

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-03-25
    • 1970-01-01
    • 2023-03-16
    • 1970-01-01
    • 1970-01-01
    • 2020-11-06
    • 1970-01-01
    相关资源
    最近更新 更多