[英]getline while reading a file vs reading whole file and then splitting based on newline character
我想现在处理硬盘上文件的每一行。 是否更好地加载整个文件,然后根据换行符(使用boost)进行拆分,还是使用getline()
更好? 我的问题是getline()
在调用时读取单行(导致多个硬盘访问)或读取整个文件并逐行提供?
getline
会将read()
作为系统调用调用到C库的最深处。 它被调用的次数及其调用方式取决于C库设计。 但是很可能一次读取一行与整个文件没有明显的区别,因为底层的操作系统一次会读取(至少)一个磁盘块,而且很可能至少是一个“页面” “(4KB),如果不是更多。
另外,unles你读完之后几乎什么都不做你的字符串(例如你正在编写类似“grep”的东西,所以大多只是读取它来找到一个字符串),一次读取一行的开销不太可能是你花费的很大一部分时间。
但是“一次性加载整个文件”有几个不同的问题:
除非您使用分析来证明代码运行缓慢的部分原因,否则不要尝试优化某些东西。 你只是为自己造成了更多问题。
编辑:所以,我写了一个测量这个的程序,因为我觉得它很有趣。
结果肯定很有趣 - 为了使比较公平,我创建了三个1297984192字节的大文件(通过使用大约十二个不同的源文件复制目录中的所有源文件,然后将该文件多次复制到“multiply”)它,直到花了超过1.5秒来运行测试,这是我认为你需要多长时间运行以确保时间不容易随机“网络数据包进入”或其他一些外部影响需要时间的过程)。
我还决定通过这个过程来衡量系统和用户时间。
$ ./bigfile
Lines=24812608
Wallclock time for mmap is 1.98 (user:1.83 system: 0.14)
Lines=24812608
Wallclock time for getline is 2.07 (user:1.68 system: 0.389)
Lines=24812608
Wallclock time for readwhole is 2.52 (user:1.79 system: 0.723)
$ ./bigfile
Lines=24812608
Wallclock time for mmap is 1.96 (user:1.83 system: 0.12)
Lines=24812608
Wallclock time for getline is 2.07 (user:1.67 system: 0.392)
Lines=24812608
Wallclock time for readwhole is 2.48 (user:1.76 system: 0.707)
这里有三个不同的函数来读取文件(当然,还有一些代码可以测量时间和内容,但是为了减少这篇文章的大小,我选择不发布所有这些 - 而且我还是订购了以查看是否这有什么不同,所以上面的结果与这里的函数的顺序不一样)
void func_readwhole(const char *name)
{
string fullname = string("bigfile_") + name;
ifstream f(fullname.c_str());
if (!f)
{
cerr << "could not open file for " << fullname << endl;
exit(1);
}
f.seekg(0, ios::end);
streampos size = f.tellg();
f.seekg(0, ios::beg);
char* buffer = new char[size];
f.read(buffer, size);
if (f.gcount() != size)
{
cerr << "Read failed ...\n";
exit(1);
}
stringstream ss;
ss.rdbuf()->pubsetbuf(buffer, size);
int lines = 0;
string str;
while(getline(ss, str))
{
lines++;
}
f.close();
cout << "Lines=" << lines << endl;
delete [] buffer;
}
void func_getline(const char *name)
{
string fullname = string("bigfile_") + name;
ifstream f(fullname.c_str());
if (!f)
{
cerr << "could not open file for " << fullname << endl;
exit(1);
}
string str;
int lines = 0;
while(getline(f, str))
{
lines++;
}
cout << "Lines=" << lines << endl;
f.close();
}
void func_mmap(const char *name)
{
char *buffer;
string fullname = string("bigfile_") + name;
int f = open(fullname.c_str(), O_RDONLY);
off_t size = lseek(f, 0, SEEK_END);
lseek(f, 0, SEEK_SET);
buffer = (char *)mmap(NULL, size, PROT_READ, MAP_PRIVATE, f, 0);
stringstream ss;
ss.rdbuf()->pubsetbuf(buffer, size);
int lines = 0;
string str;
while(getline(ss, str))
{
lines++;
}
munmap(buffer, size);
cout << "Lines=" << lines << endl;
}
操作系统将读取整个数据块(取决于磁盘的格式化,通常一次为4-8k)并为您执行一些缓冲。 让操作系统为您处理,并以对您的程序有意义的方式读取数据。
fstreams合理缓冲。 操作系统对硬盘的基础过程得到了合理的缓冲。 硬盘本身有一个合理的缓冲区。 如果逐行读取文件,最肯定不会触发更多的硬盘访问。 或者就个性而言。
因此,没有理由要整个文件加载到一个大的缓冲区,对缓冲区的工作,因为它已经是一个缓冲。 并且通常没有理由一次缓冲一行。 为什么要分配内存来缓冲已经在ifstream中缓冲的字符串中的内容? 如果可以的话,直接处理流,不要费心从一个缓冲区到下一个缓冲区扔两次或更多。 除非它支持可读性和/或您的探查器告诉您光盘访问正在大大减慢您的程序。
如果它是磁盘上的一个小文件,那么读取整个文件并逐行解析而不是一次读取一行可能会更有效 - 这将占用大量磁盘访问权限。
我相信C ++习惯用法是逐行读取文件,并在读取文件时创建基于行的容器。 很可能iostreams( getline
)将被缓冲到足以让你注意不到显着差异。
但是对于非常大的文件,通过读取较大的文件块(不是一次整个文件)并在找到换行符时拆分内部,可以获得更好的性能。
如果您想具体了解哪种方法更快,以及需要多少,则必须对代码进行分析。
如果它可以容纳在内存中,那么它可以更好地获取所有数据,因为无论何时您请求I / O,您的程序都会丢失处理并进入等待Q.
但是,如果文件大小很大,那么最好一次读取处理所需的数据。 因为较大的读操作将花费很多时间来完成小的操作。 cpu进程切换时间远小于整个文件的读取时间。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.