mmap slower than getline?
mmap slower than getline?
我面临着一行一行地读/写文件(以gb为单位)的挑战。
阅读了许多论坛条目和网站(包括一堆SO), mmap被建议作为读取/写入文件的最快选项。但是,当我同时使用readline和mmap技术实现代码时,mmap是两者中较慢的。阅读和写作都是如此。我一直在测试大约600mb大的文件。
我的实现逐行解析,然后对行进行标记。我将只显示文件输入。
下面是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();
}
,这里是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);
}
为了简洁起见,我省略了许多错误检查。
是否我的mmap实现不正确,从而影响了性能?也许mmap不适合我的应用程序?
感谢您的任何评论或帮助!
mmap的真正功能是能够自由地在文件中查找,直接将其内容用作指针,并避免将数据从内核缓存内存复制到用户空间的开销。但是,您的代码示例并没有利用这一点。
在循环中,每次扫描一个字符,附加到stringstream
。stringstream
不知道字符串有多长,因此必须在这个过程中重新分配几次。此时,您已经消除了使用mmap
带来的任何性能提升-即使是标准的getline实现也避免了多次重新分配(通过使用128字节的堆栈上缓冲区,在GNU c++实现中)。
如果你想充分发挥mmap的作用:
- 不要复制你的字符串。在所有。相反,将指针复制到mmap缓冲区中。
- 使用内置函数
strnchr
或memchr
查找换行符;它们利用手卷汇编器和其他优化来比大多数开编码搜索循环运行得更快。
告诉你使用mmap
的人不太了解现代机器。
mmap
的性能优势完全是一个神话。用Linus Torvalds的话来说:
是的,内存是"慢"的,但是该死的,mmap()也是。
mmap
的问题是,每次您第一次触摸映射区域中的页面时,它都会进入内核并实际将该页映射到您的地址空间,从而破坏TLB。
尝试一个简单的基准测试,使用read
一次读取8K的大文件,然后再使用mmap
。(反复使用相同的8K缓冲区。)您几乎肯定会发现read
实际上更快。
你的问题从来都不是从内核中获取数据;关键在于你如何处理之后的数据。尽量减少你每次所做的工作;只需扫描以找到换行符,然后在块上执行单个操作。就我个人而言,我会回到read
实现,使用(和重用)适合L1缓存(8K左右)的缓冲区。
或者至少,我会尝试一个简单的read
和mmap
基准测试,看看哪个在您的平台上实际上更快。
我发现了Torvalds先生的几组评论:
http://lkml.iu.edu/hypermail/linux/kernel/0004.0/0728.htmlhttp://lkml.iu.edu/hypermail/linux/kernel/0004.0/0775.html
摘要:
最重要的是,你仍然有实际的CPU TLB丢失成本等。如果你只是重复阅读同一领域,通常可以避免这些问题而不是过分聪明的内存管理只是避免复制。
memcpy()(即"read()"在这种情况下)是总是将更快很多情况下,只是因为它避免了所有额外的复杂性。而在其他情况下,Mmap()会更快。
根据我的经验,顺序读取和处理大文件是使用(和重用)中等大小的缓冲区并使用read
/write
的"许多情况"之一,其性能明显优于mmap
。
您可以使用memchr
来查找行结尾。这将比每次添加一个字符到stringstream
要快得多。
您正在使用stringstream
s来存储您识别的行。这与getline实现没有可比性,stringstream本身增加了开销。正如其他人建议的那样,您可以将字符串的开头存储为char*
,也可以存储为行长度(或指向行尾的指针)。read的主体类似于:
char* str_start = map;
char* str_end;
for (long i = 0; i <= FILESIZE; ++i) {
if (map[i] == 'n') {
str_end = map + i;
{
// C style tokenizing of the string str_start to str_end
// If you want, you can build a std::string like:
// std::string line(str_start,str_end);
// but note that this implies a memory copy.
}
str_start = map + i + 1;
}
}
还请注意,这是更有效的,因为您不处理每个字符中的任何内容(在您的版本中,您是将字符添加到stringstream
)。
- Seg Fault Issue C++ (file IO / getline)
- 当用户键入分隔符时,停止getline()输入
- getline() 的原型/库是什么;
- 错误 没有与参数列表匹配的重载函数"getline"实例
- 如何在 c++ 中使用 ',' 作为 getline 分隔符
- 错误:调用'getline'没有匹配函数
- 如何在 c++ 中使用 getline 从文件中读取字符串?
- 将 getline() 与文件一起使用
- 我有三个 getline,但是一旦编译,输入就太多了
- 在 std::getline 和 std::cin 期间卡在循环中
- 为什么 getline() 不读取文本文件中的所有内容?
- 有没有办法在不使用 getline() 的情况下从.csv文件中读取?
- 确切地说,如何解释 std::getline(stream, string) 函数在C++中填充的字符串
- std::getline没有在while循环中重新请求用户输入
- 谁能告诉我我用 getline 做错了什么 (cpp) 格式
- Getline(cin,str)的奇怪输出
- 我在C++中使用 getline() 函数时遇到问题
- 而循环跳过 cin.getline() 对于 C 字符串
- 如何在 getline 中删除/忽略超过特定数字的字符?
- mmap slower than getline?