通过istringstream进行c ++字符串标记的性能开销

时间:2012-06-09 12:59:18

标签: c++ optimization

我想知道

的性能开销是多少
string line, word;
while (std::getline(cin, line))
{
    istringstream istream(line);
    while (istream >> word)
        // parse word here
}

我认为这是标记化输入的标准c++方法。

具体来说:

  • 每行是否复制了三次,首先通过getline,然后通过istream构造函数,最后通过operator>>为每个单词复制?
  • 经常建设&破坏istream是个问题?如果我在外部istream循环之前定义while,那等效的实现是什么?

谢谢!

更新

等效实现

string line, word;
stringstream stream;
while (std::getline(cin, line))
{
    stream.clear();
    stream << line;
    while (stream >> word)
        // parse word here
}

使用流作为本地堆栈,推送行并弹出单词。 这将摆脱可能的频繁构造函数&amp;在前一个版本中调用析构函数,并利用流内部缓冲效果(这一点是否正确?)。

替代解决方案,可能是将std :: string扩展为支持operator<<operator>>,或者扩展iostream以支持某事。比如locate_new_line只是在这里集思广益

2 个答案:

答案 0 :(得分:6)

不幸的是,iostreams不适用于性能密集型工作。问题不在于复制内存中的内容(复制字符串很快),它的虚函数调度,可能是每个字符的几个间接函数调用。

关于复制的问题,是的,因为在初始化新的stringstream时,所有内容都会被复制。 (字符也会被getline>>从流复制到输出字符串,但显然无法阻止。)

使用C ++ 11的move工具,可以消除无关的副本:

string line, word;
while (std::getline(cin, line)) // initialize line
{       // move data from line into istream (so it's no longer in line):
    istringstream istream( std::move( line ) );
    while (istream >> word)
        // parse word here
}

所有这一切,如果测量工具告诉你,性能只是一个问题。 Iostreams具有灵活性和健壮性,filebuf基本上足够快,因此您可以对代码进行原型设计,使其工作,然后优化瓶颈,而无需重写所有内容。

答案 1 :(得分:1)

在块中定义变量时,它将在堆栈中分配。当您离开区块时,它将从堆栈中弹出。使用此代码,您可以在堆栈上进行大量操作。这也适用于“单词”。您可以使用指针并操作指针而不是变量。指针也存储在堆栈中,但它们指向的位置是堆内存中的一个位置。

此类操作可能会产生开销,用于创建变量,将其推入堆栈并再次从堆栈中弹出。但是使用指针可以分配一次空间,然后使用堆中分配的空间。因为指针可以比真实对象小得多,所以它们的分配会更快。

如您所见getLine()方法接受line对象的引用(某种指针),这使得它可以在不再创建字符串对象的情况下使用它。

在您的代码中,lineword变量只生成一次,并使用它们的引用。您在每次迭代中唯一的对象是ss变量。如果您不想在每次迭代中创建它,可以在循环之前进行,并使用其关联方法对其进行初始化。您可以搜索以找到不使用构造函数重新分配它的合适方法。

您可以使用:

string line, word ;
istringstream ss ;
while (std::getline(cin, line))
{
    ss.clear() ;
    ss.str(line) ;
    while (ss >> word) {
        // parse word here
    }
}

您也可以使用此参考istringstream

编辑:感谢评论@jrok。是的,您应该在分配新字符串之前清除错误标志。这是str()istringstream::str

的参考