C++;STL容器在用作简单缓冲区时速度很慢(我做了一个基准测试)? 我一直认为C++代码一般与C代码在效率方面是一致的,如果不是更好的话(EX:STD::由于比较器内联排序QQORT)。如果需要动态内存缓冲区,std::vector将是合理的首选,这一点也被广泛接受

C++;STL容器在用作简单缓冲区时速度很慢(我做了一个基准测试)? 我一直认为C++代码一般与C代码在效率方面是一致的,如果不是更好的话(EX:STD::由于比较器内联排序QQORT)。如果需要动态内存缓冲区,std::vector将是合理的首选,这一点也被广泛接受,c++,stl,C++,Stl,最近,我实施了一个基准来获取硬数据,并得到了令人惊讶的结果。基本上,在紧循环中附加到向量(或字符串)相对较慢 如果有可能的话,请告诉我 有关基准测试的详细信息,请参见下文 当然,一个选择是使用定制容器。出于显而易见的原因(代码的一致性加上在同一事物的不兼容表示之间转换的开销,例如std::string和自定义_字符串),我非常希望尽可能长时间地避免这种路由 关于这个问题 有一个输入缓冲区和一个输出缓冲区。如果一个输入字节通过了验证,它将按原样传输到输出,否则将对其进行编码,并传输产生的3个字节(

最近,我实施了一个基准来获取硬数据,并得到了令人惊讶的结果。基本上,在紧循环中附加到向量(或字符串)相对较慢

如果有可能的话,请告诉我

有关基准测试的详细信息,请参见下文

当然,一个选择是使用定制容器。出于显而易见的原因(代码的一致性加上在同一事物的不兼容表示之间转换的开销,例如std::string和自定义_字符串),我非常希望尽可能长时间地避免这种路由

关于这个问题

有一个输入缓冲区和一个输出缓冲区。如果一个输入字节通过了验证,它将按原样传输到输出,否则将对其进行编码,并传输产生的3个字节(这里有某种转义)。正在实现的算法是UTF-8验证

基本情况如下:

//模板化接收器允许我们使用不同的方法来构建
//用于估计各种方法的相对效率的输出
//(例如:与std::string相比,无边界检查的大缓冲区)。
模板
常量无符号字符*
固定发动机(水槽和水槽,
常量无符号字符*i,常量无符号字符*end)
{
while(i
我已经实现了fix_utf8的不同变体,包括写入预先分配的“无限”缓冲区(无边界检查或增长,基线),生成动态增长的malloc-ed缓冲区(malloc),生成std::string(string)的和产生std::vector(vector)的一个

以下是结果(常春藤桥核心i7笔记本电脑,clang-600.0.56-O3)(基于LLVM 3.5svn):

ASCII Unicode随机码 小满邪混邪短邪长 基线0.01307 0.01816 0.01912 0.01909 0.03104 0.03781 0.06127 malloc 0.01798 0.02068 0.02116 0.02095 0.03918 0.04684 0.06909 管柱0.02791 0.03045 0.02575 0.02520 0.07871 0.11513 0.09580 矢量0.06210 0.04925 0.04017 0.04027 0.10103 0.15159 0.12871 不同的列用于不同类型的随机生成的输入(Unicode small-结果UTF-8的最大范围为2个字节,evil mix-所有类型的中断UTF-8数据与正常数据交错,evil short/long-所有UTF-8序列被截断1个字节)

这些结果清楚地表明,字符串向量变体在至少两个非常重要的用例上要慢得多-ASCIIUnicode(小)


非常感谢您对改进这些结果的任何帮助!

当您了解如何使用它们时,它们的速度很快。我猜您一直在调用vector.push_back(值),它将在每次内存耗尽时重新分配新的缓冲区(通常,每次达到限制时,它的分配都会加倍)。你想先做一个vector.reserve(reasonalcapacity)。

当你了解如何使用它们时,它们会很快。我猜你一直在调用vector.push_back(value),它会在每次内存耗尽时重新分配一个新的缓冲区(通常,每次达到限制时,它的分配都会加倍)。你想先做一个vector.reserve(reasonalcapacity)。

当你了解如何使用它们时,它们会很快。我猜你一直在调用vector.push_back(value),它会在每次内存耗尽时重新分配一个新的缓冲区(通常,每次达到限制时,它的分配都会加倍)。你想先做一个vector.reserve(reasonalcapacity)。

当你了解如何使用它们时,它们会很快。我猜你一直在调用vector.push_back(value),它会在每次内存耗尽时重新分配一个新的缓冲区(通常,每次达到限制时,它的分配都会加倍)。您要执行向量。保留(合理容量)首先。

是的,你是正确的。使用这样的向量或字符串确实很慢。基本上,每次追加到容器末尾时,你都可能要求容器重新分配新缓冲区,将数据复制到新缓冲区,然后删除旧缓冲区

您可以通过两种方式改进结果。首先,如果您知道大小,可以使用
reserve()
方法。其次,如果您使用的是向量,可以切换到
deque
,这是我在对所需大小感觉不好时使用的方法。在这种情况下,这将更好地工作。这是因为deque不会重新分配内存--它会以页面的方式优雅地增长。在访问灵活性方面有一个折衷(一个间接层次),但由于您现在只是插入,所以它不应该适用

一条评论声称你的储备足够,但事实并非如此。你可以写的比你读的多。我仍然会在check_capacity()中使用与malloc策略类似的增长策略。这会给你最好的苹果
// Templated Sink allows us to play with different methods for building
// the output to estimate the relative efficiency of various approaches
// (ex: a large buffer with no bounds checking vs. std::string).
template <typename Sink>
const unsigned char *
fix_utf8_engine(Sink &sink,
                const unsigned char *i, const unsigned char *end)
{
    while (i < end) {

        // for sinks with limited capacity
        if (!sink.check_capacity())
            return i;

        switch (i[0]) {

            case 0x00 ... 0x7f:
                // 1-byte UTF-8 sequence
                sink.template write<1>(i);
                i += 1;
                continue;

...
ASCII Unicode Unicode Unicode Unicode Unicode Random small full evil mix evil short evil long baseline 0.01307 0.01816 0.01912 0.01909 0.03104 0.03781 0.06127 malloc 0.01798 0.02068 0.02116 0.02095 0.03918 0.04684 0.06909 string 0.02791 0.03045 0.02575 0.02520 0.07871 0.11513 0.09580 vector 0.06210 0.04925 0.04017 0.04027 0.10103 0.15159 0.12871
// Write output to a container
template < typename CONTAINER >
struct std_sink
{
    CONTAINER &v_;
    std_sink(CONTAINER &v): v_(v) {}
    bool check_capacity() { 
        if (v_.capacity() - v_.size() <= 8) {
            v_.reserve(v_.size() + (v_.size() / 2));
        }
        return true;
    }
    template<size_t n> void write(const unsigned char *p) {
        v_.insert(v_.end(), p, p+n);
    }
    void write_bad(const unsigned char *p) {
        unsigned char esc[] = {
            utf8b_1(*p),
            utf8b_2(*p),
            utf8b_3(*p)
        };
        v_.insert(v_.end(), esc, esc + 3);
    }
};
std_sink< std::deque< unsigned char> > sink(result);
std::string output;
char *opos; // output pos
char *end_obuf;

while (...) {

   if (opos + 6 > end_obuf) { // 6 bytes produced per iteration or less
      output.resize(s.size() + 128);
      // reinit opos, end_obuf pointers
      ...
   }

   // processing
   ...
}