【问题标题】:allocating memory using push_back vs constructing vectors of specific size使用 push_back 分配内存与构造特定大小的向量
【发布时间】:2018-12-21 08:47:11
【问题描述】:

在 C++ Primer 中找到以下语句

由于向量的增长效率很高,它通常是不必要的——而且可以 导致性能下降——定义一个特定大小的向量。 此规则的例外情况是,如果所有元素实际上都需要 相同的值。如果需要不同的元素值,通常需要更多 有效地定义一个空向量并将元素添加为我们的值 需要在运行时知道。

我怀疑如果您事先保留内存,则不需要重新分配(这被认为是一个较慢的过程)。那么如何使用 push_back 带来更好的性能呢?

【问题讨论】:

    标签: c++ performance vector


    【解决方案1】:

    我认为作者是在比较以下两种情况:

    int n = ...;
    std::vector<...> v(n);
    for (auto& x : v) x = some_value_known_at_runtime;
    

    int n = ...;
    std::vector<...> v;
    for (int i = 0; i < n; i++) v.push_back(some_value_known_at_runtime);
    

    第一种情况将使用n 默认构造的元素构造vector,然后再分配它们;这可能会导致性能下降。

    当然,你可以在第二种情况下使用reserve,这样可以避免重新分配,提高效率;如果可以提前知道元素的数量。

    int n = ...;
    std::vector<...> v;
    v.reserve(n);
    for (int i = 0; i < n; i++) v.push_back(some_value_known_at_runtime);
    

    【讨论】:

      【解决方案2】:

      这取决于您在默认构造函数中所做的工作。如果您的对象在默认构造函数中做了一些昂贵的事情,您的程序可以在向量分配中冻结一段时间。相反,push_back 会为每个对象调用一次复制构造函数,并且增量(或及时)调用。那可能会更快。

      但是,重新分配和批量复制也可能代价高昂,特别是如果您的对象是许多对象的组合等等,所以在这种情况下,只有分析才能知道什么是最好的。

      另一方面,如果默认构造函数什么都不做,那么在大多数情况下避免重新分配会赢。

      【讨论】:

        【解决方案3】:

        我在 C++ 入门中看到了这句话,他在以下上下文中这么说。

        // read words from the standard input and store them as elements in a vector 
        string word;
        vector<string> text; // empty vector
        while (cin >> word) {
            text.push_back(word); // append word to text 
        }
        

        这里的向量每次都以不同的值增长未知大小。

        此外,他说的是“经常不必要”而不是“总是”。我对其进行了已知次数的测试,并且在所有情况下,reserve 的表现都更好。

        看下面的Vector实现sn-p会更好理解。

        template<typename T> 
        class Vector {
            T∗ elem;  // pointer to first element
            T∗ space; // pointer to first unused (and uninitialized) slot 
            T∗ last;  // pointer to last slot
        public: 
            // …
            int size();      // number of elements (space-elem)
            int capacity();  // number of slots available for elements (last-elem)
            // ...
            void reserve(int newsz); // increase capacity() to newsz
            // ...
           void push_back(const T& t); // copy t into Vector
        };
        
        template<typename T>
        void Vector<T>::push_back(const T& t) { 
            if (capacity()<size()+1)            // make sure we have space for t 
                reserve(size()==0?8:2∗size());  // double the capacity 
            new(space){t};                      // initialize *space to t 
            ++space; 
        } 
        

        【讨论】:

          【解决方案4】:

          需要考虑的因素很少。

          您是否经常使用同一个向量,我的意思不是 5 次或 10 次,而是数千次。原因是您希望尽可能重用该对象以避免不必要的分配和释放。为此,您可以使用clear

          向量是否可能总是有一定数量的元素,那么您可以使用reserve 命令预先分配元素。

          一般来说,如果你有一个向量作为永久存储,很少改变内容,你不应该为这些事情烦恼。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 2021-04-23
            • 2012-03-25
            • 1970-01-01
            • 1970-01-01
            • 2013-03-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多