为什么VS2008 std :: string.erase()会移动它的缓冲区?

时间:2011-11-17 17:49:17

标签: c++ visual-studio-2008 c++-standard-library

我想逐行读取文件并捕获一行特定的输入。为了获得最大性能,我可以通过读取整个文件并使用指针迭代其内容以低级方式执行此操作,但此代码对性能不重要,因此我希望使用更具可读性和类型安全的标准库样式实现。

所以我拥有的是:

 std::string line;
 line.reserve(1024);
 std::ifstream file(filePath);
 while(file)
 {
    std::getline(file, line);
    if(line.substr(0, 8) == "Whatever")
    {
        // Do something ...
    }
 }

虽然这不是性能关键代码,但我在解析操作之前调用了line.reserve(1024),以便在读入更大的行时排除字符串的多次重新分配。

在std :: getline内部删除字符串,然后将每行中的字符添加到其中。我逐步完成了这段代码,以确保每次迭代都没有重新分配内存,我发现这些内容让我的大脑充满了热情。

深入string :: erase而不是仅仅将其size变量重置为零实际上正在做的是调用带有指针值的memmove_s,该指针值会覆盖缓冲区的使用部分,紧跟其后的缓冲区的未使用部分,除了正在使用count参数为零调用memmove_s,即请求移动零字节。

问题:

为什么我想在我可爱的循环中调用库函数调用的开销,特别是那个被调用什么都没做的东西呢?

我自己还没有选择它,但在什么情况下这个调用实际上什么都不做,但实际上会开始移动大块的缓冲区?

为什么这样做呢?

奖金问题:C ++标准库标签是什么?

3 个答案:

答案 0 :(得分:11)

这是我一年前报道的一个已知问题,利用您必须升级到未来版本的编译器的修复程序。

Connect Bug: "std::string::erase is stupidly slow when erasing to the end, which impacts std::string::resize"

除了std::string之外,该标准没有说明任何swap函数的复杂性。

答案 1 :(得分:3)

std::string::clear()是根据std::string::erase()定义的, 并且std::string::erase()必须移动所有字符 被删除的块。那么为什么它不应该称之为标准 这样做的功能?如果你有一些探查器输出证明了这一点 这是一个瓶颈,那么也许你可以抱怨它,但是 坦率地说,我看不出它有所作为。 (逻辑 避免通话所需的费用最终可能比通话费用更高。)

此外,您之前没有查看对getline的通话结果 使用它们。你的循环应该是这样的:

while ( std::getline( file, line ) ) {
    //  ...
}

如果你对性能如此担心,那就创建一个子串(一个新的 std::string)只是为了进行比较要贵得多 而不是拨打memmove_s。有什么问题:

static std::string const target( "Whatever" );
if ( line.size() >= target.size()
        && std::equal( target.begin(), target().end(), line.being() ) ) {
    //  ...
}

我认为这是确定是否是最常用的方法 string以特定值开头。

(我可以根据经验补充一点,reserve这里不会给你买 很多。在您阅读文件中的几行后,您的 无论如何,字符串不会增长太多,所以很少 在前两行之后重新分配。另一个案例 过早优化?)

答案 2 :(得分:0)

在这种情况下,我认为你提到的阅读整个文件并迭代结果的想法实际上可以提供简单的代码。您只需更改:"读取行,检查前缀,处理" to"读取文件,扫描前缀,处理":

size_t not_found = std::string::npos;
std::istringstream buffer;

buffer << file.rdbuf();

std::string &data = buffer.str();

char const target[] = "\nWhatever";
size_t len = sizeof(target)-1;

for (size_t pos=0; not_found!=(pos=data.find(target, pos)); pos+=len)
{
    // process relevant line starting at contents[pos+1]
}