C++ 如何加快计算大文件中单词的出现次数?
我需要计算104gb文件中字符串C++ 如何加快计算大文件中单词的出现次数?,c++,performance,optimization,file-io,C++,Performance,Optimization,File Io,我需要计算104gb文件中字符串“的出现次数,以获取给定Wikipedia转储中的文章数。首先,我试过这个 grep -F '<page>' enwiki-20141208-pages-meta-current.xml | uniq -c grep-F''enwiki-20141208-pages-meta-current.xml | uniq-c 然而,grep在一段时间后崩溃了。因此,我编写了以下程序。然而,它在我的机器上只处理20mb/s的输入文件,这大约是我硬盘的5%的工
“
的出现次数,以获取给定Wikipedia转储中的文章数。首先,我试过这个
grep -F '<page>' enwiki-20141208-pages-meta-current.xml | uniq -c
grep-F''enwiki-20141208-pages-meta-current.xml | uniq-c
然而,grep在一段时间后崩溃了。因此,我编写了以下程序。然而,它在我的机器上只处理20mb/s的输入文件,这大约是我硬盘的5%的工作负载。如何加速此代码
#include <iostream>
#include <fstream>
#include <string>
int main()
{
// Open up file
std::ifstream in("enwiki-20141208-pages-meta-current.xml");
if (!in.is_open()) {
std::cout << "Could not open file." << std::endl;
return 0;
}
// Statistics counters
size_t chars = 0, pages = 0;
// Token to look for
const std::string token = "<page>";
size_t token_length = token.length();
// Read one char at a time
size_t matching = 0;
while (in.good()) {
// Read one char at a time
char current;
in.read(¤t, 1);
if (in.eof())
break;
chars++;
// Continue matching the token
if (current == token[matching]) {
matching++;
// Reached full token
if (matching == token_length) {
pages++;
matching = 0;
// Print progress
if (pages % 1000 == 0) {
std::cout << pages << " pages, ";
std::cout << (chars / 1024 / 1024) << " mb" << std::endl;
}
}
}
// Start over again
else {
matching = 0;
}
}
// Print result
std::cout << "Overall pages: " << pages << std::endl;
// Cleanup
in.close();
return 0;
}
#包括
#包括
#包括
int main()
{
//打开文件
std::ifstream in(“enwiki-20141208-pages-meta-current.xml”);
如果(!in.is_open()){
std::cout假设文件中没有使用
for (std::string line; std::getline(in, line); } {
// find the number of "<page>" strings in line
}
对于仍然做得太多的流的错误实现。好的实现将调用std::find(…)
非常有效,并且可能一次检查多个字符,仅为每16次循环迭代添加一个检查和循环。我希望上面的代码将CPU绑定的实现转变为I/O绑定的实现。糟糕的实现可能仍然是CPU绑定的,但应该会更好
在任何情况下,请记住启用优化!我使用此文件测试:
使用代码大约需要2.4秒,而使用代码需要11.5秒。由于不计算换行符,字符总数略有不同,但我认为这是可以接受的,因为它仅用于显示进度
void parseByLine()
{
// Open up file
std::ifstream in("enwiki-latest-pages-meta-current1.xml-p000000010p000010000");
if(!in)
{
std::cout << "Could not open file." << std::endl;
return;
}
size_t chars = 0;
size_t pages = 0;
const std::string token = "<page>";
std::string line;
while(std::getline(in, line))
{
chars += line.size();
size_t pos = 0;
for(;;)
{
pos = line.find(token, pos);
if(pos == std::string::npos)
{
break;
}
pos += token.size();
if(++pages % 1000 == 0)
{
std::cout << pages << " pages, ";
std::cout << (chars / 1024 / 1024) << " mb" << std::endl;
}
}
}
// Print result
std::cout << "Overall pages: " << pages << std::endl;
}
void parseByLine()
{
//打开文件
std::ifstream in(“enwiki-latest-pages-meta-current1.xml-p000000010p000010000”);
如果(!in)
{
std::没问题,但你应该阅读。一次读一行可能比一个字符好。我假设你是在优化版本中测试这个,如果不是的话,你应该这样做。@CaptainObvlious谢谢,修复了。你试过split
然后fgrep
?你的瓶颈应该是文件输入。为了提高性能,more数据应该在搜索之前读取到内存中。一个线程将数据读入缓冲区,另一个线程搜索缓冲区,可能会获得一些额外的性能。谢谢,读取行是一个巨大的改进。我现在得到115mb/s。但是,这仍然只会导致硬盘激活时间的25%。你说的大量行是什么意思?我相信整个Wikipedia文章都存储在一行中。当一行比可用缓存大时,它将被读入缓存,被逐出,然后带回来寻找字符串。如果有时发生这种情况并不好,但如果频繁发生则不好。如果该行甚至比可用的主内存大,它甚至会被删除e从内存中退出并读取多次。对于典型长度为几kB的行,这可能没问题,对于只有几MB的行,这可能已经不好了。我不能只读取固定大小的块,这样行长度就不再重要了吗?我可以像任何其他字符一样读取固定大小的块。当然,您可以读取固定大小的块。但是,请记住,您需要检查是否有拆分为两页的“
。此外,这样做还需要将缓冲区复制到新位置。使用std::find()
使用std::istreambuf_迭代器
可以在流缓冲区的缓冲区中定位起始字符,从而避免额外的副本。很好,检查拆分为两个块的令牌对我的算法没有问题。我只需在块之间保持匹配的值。固定大小的块给我140mb/s和90%的disk活动时间。我想这是我能做到的,或者你有其他想法吗?
void parseByLine()
{
// Open up file
std::ifstream in("enwiki-latest-pages-meta-current1.xml-p000000010p000010000");
if(!in)
{
std::cout << "Could not open file." << std::endl;
return;
}
size_t chars = 0;
size_t pages = 0;
const std::string token = "<page>";
std::string line;
while(std::getline(in, line))
{
chars += line.size();
size_t pos = 0;
for(;;)
{
pos = line.find(token, pos);
if(pos == std::string::npos)
{
break;
}
pos += token.size();
if(++pages % 1000 == 0)
{
std::cout << pages << " pages, ";
std::cout << (chars / 1024 / 1024) << " mb" << std::endl;
}
}
}
// Print result
std::cout << "Overall pages: " << pages << std::endl;
}
int processBuffer(const std::string& buffer)
{
static const std::string token = "<page>";
int pages = 0;
size_t pos = 0;
for(;;)
{
pos = buffer.find(token, pos);
if(pos == std::string::npos)
{
break;
}
pos += token.size();
++pages;
}
return pages;
}
void parseByMB()
{
// Open up file
std::ifstream in("enwiki-latest-pages-meta-current1.xml-p000000010p000010000");
if(!in)
{
std::cout << "Could not open file." << std::endl;
return;
}
const size_t BUFFER_THRESHOLD = 16 * 1024 * 1024;
std::string buffer;
buffer.reserve(BUFFER_THRESHOLD);
size_t pages = 0;
size_t chars = 0;
size_t progressCount = 0;
std::string line;
while(std::getline(in, line))
{
buffer += line;
if(buffer.size() > BUFFER_THRESHOLD)
{
pages += processBuffer(buffer);
chars += buffer.size();
buffer.clear();
}
if((pages / 1000) > progressCount)
{
++progressCount;
std::cout << pages << " pages, ";
std::cout << (chars / 1024 / 1024) << " mb" << std::endl;
}
}
if(!buffer.empty())
{
pages += processBuffer(buffer);
chars += buffer.size();
std::cout << pages << " pages, ";
std::cout << (chars / 1024 / 1024) << " mb" << std::endl;
}
}