代码之家  ›  专栏  ›  技术社区  ›  Frank

对容器(循环期间)重复调用size()是否错误?

  •  12
  • Frank  · 技术社区  · 15 年前

    出于效率考虑,我总是避免这样写循环:

    for(std::size_t i = 0; i < vec.size(); ++i) { ... }
    

    哪里 vec 是STL容器。相反,我要么

    const std::size_t vec_size = vec.size();
    for(std::size_t i = 0; i < vec_size; ++i) { ... }
    

    但第一种解决方案到底有多糟呢?我记得在梅耶斯的书中读到过,它将是二次的而不是线性的,因为向量不知道它的大小,必须反复计算。但是,现代的编译器不会检测到这一点并对其进行优化吗?

    4 回复  |  直到 9 年前
        1
  •  10
  •   Marcelo Cantos    15 年前

    vector::size() 是一个常数时间,通常作为一个简单的内联函数来实现。不用费心手动优化。

        2
  •  9
  •   Billy ONeal IS4    15 年前

    你越来越 vector list 困惑的。 矢量

        3
  •  1
  •   Borealid    15 年前

    判断编译器是否正在优化某些内容的最简单方法是比较汇编语言编译器的输出。

    也就是说,这两段代码实际上并不等价。如果在迭代过程中向量的大小发生了变化呢?编译器必须非常非常聪明才能最终证明向量的大小 改变。

    vec.size() 只返回一个存储值。它不会重新计算长度。

        4
  •  1
  •   celion    15 年前

    void sum (vector<int>& vec, int* sumOut)
    {
        *sumOut = 0;
        for(std::size_t i = 0; i < vec.size(); ++i)
        {
            *sumOut += vec[i];      
        }
    }
    

    生成的实际程序集将取决于编译器和的实现 vector ,但我认为在大多数情况下,编译器必须重新读取 矢量 sumOut 指针可能会重叠(别名)向量大小的内部存储(假设 矢量 将其大小存储在int中),因此大小可以由循环更改。如果你经常调用这样的函数,它可能会累积很多周期,因为你接触内存的次数超出了你的需要。

    1. 将大小存储在局部变量中。 理想情况下,这个尺寸 存放在寄存器中,避免触摸 放在堆栈上,编译器 更有效地加载/存储。

    2. 使用 __restrict 在输出端 指针。这会告诉编译器 指针不可能 它不需要重新加载任何东西 否则。

    3. 反转循环。终止 条件现在检查0 vec.size() 永远不会 又打电话来了。

    其中,我认为#1是最干净的,但有些人可能更喜欢#3。#2可能是最不方便阅读的,但可能比其他的更快(因为这意味着向量的数据可以更有效地读取)。

    Christer Ericson's GDC presentation on memory optimization ; 这里有一个和这个差不多的例子。