mmap比getline慢?

Ian*_*Ian 5 c++ file-io mmap getline

我面临着逐行读取/写入文件(在Gigs中)的挑战.

读取许多论坛条目和网站(包括一堆SO),mmap被认为是读/写文件的最快选项.但是,当我用readline和mmap技术实现我的代码时,mmap是两者中较慢的一个.这对于阅读和写作都是如此.我一直在测试大约600 MB的文件.

我的实现逐行解析,然后对行进行标记.我只会提供文件输入.

以下是getline实现:

void two(char* path) {

    std::ios::sync_with_stdio(false);
    ifstream pFile(path);
    string mystring;

    if (pFile.is_open()) {
        while (getline(pFile,mystring)) {
            // c style tokenizing
        }
    }
    else perror("error opening file");
    pFile.close();
}
Run Code Online (Sandbox Code Playgroud)

这是mmap:

void four(char* path) {

    int fd;
    char *map;
    char *FILEPATH = path;
    unsigned long FILESIZE;

    // find file size
    FILE* fp = fopen(FILEPATH, "r");
    fseek(fp, 0, SEEK_END);
    FILESIZE = ftell(fp);
    fseek(fp, 0, SEEK_SET);
    fclose(fp);

    fd = open(FILEPATH, O_RDONLY);

    map = (char *) mmap(0, FILESIZE, PROT_READ, MAP_SHARED, fd, 0);

    /* Read the file char-by-char from the mmap
     */
    char c;
    stringstream ss;

    for (long i = 0; i <= FILESIZE; ++i) {
        c = map[i];
        if (c != '\n') {
            ss << c;
        }
        else {
            // c style tokenizing
            ss.str("");
        }

    }

    if (munmap(map, FILESIZE) == -1) perror("Error un-mmapping the file");

    close(fd);

}
Run Code Online (Sandbox Code Playgroud)

为了简洁起见,我省略了很多错误检查.

我的mmap实现是否不正确,从而影响性能?也许mmap不适合我的应用程序?

感谢您的任何意见或帮助!

bdo*_*lan 11

mmap的真正强大之处在于能够在文件中自由搜索,直接将其内容用作指针,并避免将数据从内核缓存内存复制到用户空间的开销.但是,您的代码示例没有利用此功能.

在循环中,您一次扫描缓冲区一个字符,附加到stringstream.在stringstream不知道字符串是怎么长,所以在过程中重新分配多次.此时,您已经消除了使用中的任何性能提升mmap- 即使标准的getline实现也避免了多次重新分配(在GNU C++实现中使用128字节的堆栈缓冲区).

如果你想使用mmap最大的力量:

  • 不要复制你的字符串.完全没有.相反,将指针复制到mmap缓冲区中.
  • 使用内置函数,如strnchrmemchr查找换行符; 这些使用手动汇编程序和其他优化比大多数开放编码的搜索循环运行得更快.


Nem*_*emo 6

告诉你使用的mmap人对现代机器知之甚少.

性能优势mmap是一个总的神话.用Linus Torvalds话说:

是的,记忆是"慢",但是该死,mmap()也是如此.

问题mmap在于,每当您第一次触摸映射区域中的页面时,它都会陷入内核并实际将页面映射到您的地址空间,从而对TLB造成严重破坏.

尝试一个简单的基准测试,一次读取8K大文件read,然后再使用mmap.(反复使用相同的8K缓冲区.)你几乎肯定会发现它read实际上更快.

你的问题永远不会从数据中获取数据; 这是因为你在那之后如何处理数据.最大限度地减少您每次都在做的工作; 只需扫描以找到换行符,然后对该块执行单个操作.就个人而言,我会回到read实现,使用(并重新使用)适合L1缓存(8K左右)的缓冲区.

或者至少,我会尝试一个简单的read对比mmap基准,看看这实际上是你的平台上更快.

[更新]

我找到了Torvalds先生的几套评论:

http://lkml.iu.edu/hypermail/linux/kernel/0004.0/0728.html http://lkml.iu.edu/hypermail/linux/kernel/0004.0/0775.html

摘要:

最重要的是,你仍然有实际的CPU TLB未命中成本等.如果你只是重新读入同一区域而不是过于聪明地使用内存管理来避免复制,这通常可以避免.

memcpy()(即本例中的"read()")在许多情况下总是会更快,只是因为它避免了所有额外的复杂性.虽然mmap()在其他情况下会更快.

根据我的经验,阅读和处理大文件顺序是的"很多情况下,"一个地方使用(和再利用)具有中等规模的缓存read/ write性能比显著更好mmap.