string str1, str2;
vector<string> vec;
ifstream infile;
infile.open("myfile.txt");
while (! infile.eof() )
{
getline(infile,str1);
istringstream is;
is >> str1;
while (is >> str2)
{
vec.push_back(str2);
}
}
代码所做的是从文件中读取字符串并将其存储到向量中。
表现需要优先考虑。如何优化此代码,使读取性能更快?
答案 0 :(得分:4)
正如其他人已经指出的那样(例如参见herohuyongtao's answer),必须修复循环条件以及如何将str1
放入istringstream
。
然而,这里有一个重要的问题,到目前为止每个人都错过了:你根本不需要istringstream
!
vec.reserve(the_number_of_words_you_exptect_at_least);
while (infile >> str1) {
vec.push_back(str1);
}
它摆脱了你首先不需要的内部循环,并且在每次迭代中都没有创建istringstream
。
如果你需要进一步解析每一行,你需要一个istringstream
,在循环外面创建并通过istringstream::str(const string& s)
设置它的字符串缓冲区。
我可以很容易地想象你的循环非常慢:Windows上的堆分配非常慢(与Linux相比);我被咬了一次。
Andrei Alexandrescu(在某种意义上)在他的演讲Writing Quick Code in C++, Quickly中提出了一个类似的例子。令人惊讶的是,在如上所述的紧密循环中执行不必要的堆分配可能比实际文件IO慢。我很惊讶地看到了这一点。
你没有将你的问题标记为C ++ 11,但这是我在C ++ 11中所做的。
while (infile >> str1) {
vec.emplace_back(std::move(str1));
}
此移动在向量的后面构造字符串,而不进行复制。我们可以这样做,因为在将它放入向量之后我们不需要str1
的内容。换句话说,不需要将它复制到矢量背面的全新字符串中,只需将其内容移动到那里就足够了。 vec.push_back(str1);
的第一个循环可能可能会复制str1
的内容,这实际上是不必要的。
gcc 4.7.2中的字符串实现目前是copy on write,因此两个循环具有相同的性能;你使用哪一个并不重要。现在。
不幸的是,标准现在禁止复制写入字符串。我不知道gcc开发人员什么时候会改变实现。如果实施方式发生变化,无论您是移动(emplace_back(std::move(s))
)还是复制(push_back(s)
),都可能会对性能产生影响。
如果C ++ 98兼容性对您很重要,请使用push_back()
。即使将来最糟糕的事情发生并且您的字符串被复制(现在没有被复制),该副本也可以变成memmove()
/ memcpy()
,这种速度非常快,很可能比从硬盘读取文件的内容,因此文件IO很可能仍然是瓶颈。
答案 1 :(得分:2)
在进行任何优化之前,您需要更改
while (! infile.eof() ) // problem 1
{
getline(infile,str1);
istringstream is;
is >> str1; // problem 2
while (is >> str2){
vec.push_back(str2);
}
}
到
while ( getline(infile,str1) ) // 1. don't use eof() in a while-condition
{
istringstream is(str1); // 2. put str1 to istringstream
while (is >> str2){
vec.push_back(str2);
}
}
使其按预期工作。
P.S。对于优化部分,除非它成为瓶颈,否则您不需要过多考虑它。 Premature optimization is the root of all evil。但是,如果您想加快速度,请查看@Ali的答案以获取更多信息。
答案 2 :(得分:1)
Loop condition is wrong.不是性能问题。假设这个IO循环确实是你的应用程序的瓶颈。但即使不是,它也可以是一次很好的教育活动,也可以只是周末的乐趣。
你在循环中有很多临时和动态内存分配的情况。
在循环前调用std::vector::reserve()
会稍微改善一下。手动重新分配以模拟x1.2增长因子,在某种尺寸后反对2倍也会有所帮助。如果文件大小不可预测,std::list
可能更合适。
使用std::istringstream
作为标记器非常 inoptimal。切换到基于迭代器的“视图”标记器(Boost
has one)应该可以提高速度。
如果您需要快速非常并且有足够的RAM,您可以在读取文件之前对内存进行映射。 Boost::iostreams可以让你快速到达那里。一般来说,如果没有Boost,你可以快两倍(Boost不错,但它必须是通用的,并且可以在十几个编译器上工作)。
如果你是一个使用Unix / Linux的有福的人,你的开发环境在valgrind --tool=cachegrind
下运行你的程序,你会看到所有有问题的地方以及它们相对于另一个有多糟糕。此外,valgrind --tool=massif
将允许您识别nubmerous小堆分配的对象,这在高性能代码中通常是不可容忍的。
答案 3 :(得分:1)
最快但不完全可移植的方法是将文件加载到内存映射区域(请参阅wiki mmap。
鉴于您知道文件的大小,您现在可以在该内存区域上定义前向迭代器(可能是指向const char
的指针),您可以使用它来查找将文件分隔为“字符串”的标记。
基本上,你反复得到一对指向第一个字符的指针,分别指向每个“字符串”的结尾。从这对迭代器中创建std::string
。
这种方法虽然有微妙的问题:
您需要处理文件的字符编码,可能会将此字符编码转换为std::string
(可能的UTF-8)所使用的所需编码。
用于分隔字符串的“标记”(通常为\n
)可能与平台有关,也可能取决于创建该文件的程序。