【问题标题】:Should I preallocate std::stringstream?我应该预先分配 std::stringstream 吗?
【发布时间】:2009-12-21 16:23:52
【问题描述】:

我广泛使用std::stringstream 在我的应用程序中构造字符串和错误消息。 stringstreams 通常是寿命很短的自动变量。

这样的使用会导致每个变量的堆重新分配吗?我应该从临时变量切换到类成员stringstream 变量吗?

在后一种情况下,如何保留stringstream 缓冲区? (我应该用足够大的字符串初始化它还是有更优雅的方法?)

【问题讨论】:

    标签: c++ stl stringstream


    【解决方案1】:

    您是否分析过您的执行情况,并发现它们是导致减速的原因?

    考虑它们的用法。它们主要用于代码正常流程之外的错误消息吗?

    至于预留空间...

    某些实现可能会在对字符串流进行任何分配之前保留一个小缓冲区。 std::string 的许多实现都是这样做的。

    另一个选项可能是(未经测试!)

    std::string str;
    str.reserve(50);
    std::stringstream sstr(str);
    

    您可能会在此gamedev thread 中找到更多想法。

    编辑:

    使用stringstream's rdbuf 也可能是一个解决方案。这种方法可能很容易出错,所以请be sure it's absolutely necessary。绝对不优雅或简洁。

    【讨论】:

    • 好建议;但是,保留 50 然后复制该字符串可能会破坏一个小的非堆缓冲区优化(如果您的实现有一个)。如果您想编写自己的 streambuf,那么您可以精确控制所有缓冲区管理;然后,您将使用 ostream(或 istream 或 iostream,但问题表明此处关注的是输出格式)与您的 streambuf,而不是通过 rdbuf 更改字符串流的缓冲区。
    • 我认为 stringstream 保留的内容不会超过 str 的内容长度(即 0)。根据 cplusplus.com:“构造一个以 str 的副本作为内容的 ostringstream 对象。”。参考 sais “字符串内容的副本”。它没有明确说它保留了相同数量的内存。我找不到任何说明使用此构造函数保留内存的任何来源。
    • 你上面使用的构造函数不再有效。您可以看到无复制构造函数已被删除。 cplusplus.com/reference/sstream/stringstream/stringstream
    • 你可以用 C++20 做到这一点:std::stringstream sstr(std::move(str));或者如果您完成了类似 std::move(sstr).str() 的字符串流,则在提取结果时避免字符串复制
    【解决方案2】:

    虽然“乱搞stringstream's rdbuf...可能很容易出错”,但我还是继续使用hacked together a proof-of-concept 来取乐,因为它一直困扰着我,@987654324 没有简单的方法@存储为stringstream。同样,正如@luke 所说,优化分析器告诉您需要优化的内容可能会更好,所以这只是为了解决“如果我想这样做怎么办?”。

    我没有与stringstream's rdbuf 混在一起,而是自己做了一个,它的作用几乎相同。它只实现最小值,并使用string 作为缓冲区。不要问我为什么称它为VECTOR_output_stream。这只是一个快速破解的东西。

    constexpr auto preallocated_size = 256;
    auto stream = vector_output_stream(preallocated_size);
    stream << "My parrot ate " << 3 << " cookies.";
    cout << stream.str() << endl;
    

    【讨论】:

      【解决方案3】:

      我不确定,但我怀疑stringstream 的stringbuf 与结果string 密切相关。所以我怀疑你可以使用ss.seekp(reserved-1); ss.put('\0'); 来保留ss 的底层string 内的reserved 字节数。其实我想看ss.seekp(reserved); ss.trunc();之类的东西,但是没有trunc()流的方法。

      【讨论】:

      • 我已经在Visual Studio 2015 Update 3 中对其进行了测试,并在接缝处提高了badbit。后面的 &lt;&lt; 操作符会失败。
      • 我通常不信任 MSVC 的行为。但是根据seekp 和seekpos,我认为没有要求支持或不支持该操作。因此,我猜你不能依赖该代码在不同的编译器+运行时同样工作。
      【解决方案4】:

      坏人

      这是一个老问题,但即使在 Visual Studio 2019 中的 C++1z/C++2a 中,stringstream 也没有保留缓冲区的理想方法。

      这个问题的其他答案根本不起作用,原因如下:

      • 对空字符串调用reserve会产生一个空字符串,因此stringstream构造函数不需要分配来复制该字符串的内容。

      • stringstream 上的seekp 似乎仍然是未定义的行为和/或什么都不做。

      善

      此代码段按预期工作,ss 被预分配了请求的大小。

      std::string dummy(reserve, '\0');
      std::stringstream ss(dummy);
      dummy.clear();
      dummy.shrink_to_fit();
      

      代码也可以写成单行std::stringstream ss(std::string(reserve, '\0'));。

      丑陋的

      此代码段中真正发生的情况如下:

      • dummy 预分配了预留空间,随后缓冲区填充了空字节(构造函数需要)。
      • stringstream 是用假人构造的。这会将整个字符串的内容复制到预先分配的内部缓冲区中。
      • dummy 然后被清除然后擦除,释放其分配。

      这意味着为了预分配stringstream,需要进行两次分配、一次填充和一次复制。最糟糕的是,在表达式期间,所需的分配需要两倍的内存。哎呀!

      对于大多数用例来说,这可能根本不重要,并且可以采取额外的填充和复制命中来减少重新分配。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-08-06
        • 2019-01-29
        • 2011-09-09
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多