CPPapprentice
CPPapprentice

Reputation: 93

Vector: clearing it vs deleting it

I'm new to c++.

In every frame, I want to fill up a vector with data from different instances and process this data in a certain order, then rinse and repeat. This vector is going to be fairly large (with possibly 800+ elements),

My question is:

Is it faster to clear a vector than to just delete it and make a new one (of appropriate size to avoid allocations) each frame?

PS: The data being processed is dynamic hence why I need a clean state each frame.

Upvotes: 3

Views: 624

Answers (2)

James Poag
James Poag

Reputation: 2380

Resize your vector to the size you think you'll need for the frame and keep a size_t mCursor=0

When you insert, increment the mCursor++; when you 'clear' for the next frame just set it to mCursor=0.


If you avoid resizing the vector during the frame, then the data will be at the same memory location for the duration of the frame. Also, if you cap out (mCursor >= vector.size), just drop the data and remember how many sprites you dropped. Then resize the vector between frames.

Keep a Max counter for the cursor so you can update your source to start out with the larger vector. At some point though, you might want to drop sprites, especially if you overload it with particles.


Perf Code:

// SpeedTestClearVector.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include <iostream>
#include <time.h>
#include <vector>


namespace sf
{
    class Texture;
    struct IntRect { int rect[4]; };
    struct FloatRect { float rect[4]; };
    struct RenderTarget { };
    struct Vertex { float verts[3]; float uv[2]; };
    enum RenderStates {};
    typedef IntRect Color;

    class Sprite // : public Drawable, public Transformable
    {
    public:
        Sprite() {}
        explicit Sprite(const Texture& texture) {}
        Sprite(const Texture& texture, const IntRect& rectangle) {}
        void setTexture(const Texture& texture, bool resetRect = false) {}
        void setTextureRect(const IntRect& rectangle) {}
        void setColor(const Color& color) {}
        const Texture* getTexture() const {}
        const IntRect& getTextureRect() const {}
        const Color& getColor() const {}
        FloatRect getLocalBounds() const {}
        FloatRect getGlobalBounds() const {}

    private:
        virtual void draw(RenderTarget& target, RenderStates states) const {}
        void updatePositions() {}
        void updateTexCoords() {}

        Vertex         m_vertices[4]; ///< Vertices defining the sprite's geometry
        const Texture* m_texture;     ///< Texture of the sprite
        IntRect        m_textureRect; ///< Rectangle defining the area of the source texture to display
    };

} // namespace sf


int main()
{
    std::cout << "Hello World" << std::endl;

    const size_t reserve_size = 1024;
    const size_t iterations = 1000000;

    // Vector Clear
    {
        std::vector<sf::Sprite> spriteCache;
        spriteCache.reserve(reserve_size);

        time_t start_time = time(nullptr);

        for (size_t n = 0; n < iterations; ++n)
        {
            spriteCache.resize(reserve_size);
            spriteCache.clear();
        }

        auto total_time = difftime(time(nullptr), start_time);
        std::cout << "vector::clear() took " << total_time << " seconds of your life that you cannot have back." << std::endl;
    }
    // Output:
    // vector::clear() took 6 seconds of your life that you cannot have back.

    // Vector Cursor
    {
        std::vector<sf::Sprite> spriteCache;
        spriteCache.resize(reserve_size);
        size_t mCursor = 0;

        time_t start_time = time(nullptr);

        for (size_t n = 0; n < iterations; ++n)
        {
            spriteCache.resize(reserve_size); // does nothing, but included to be similar to previous test
            mCursor = reserve_size;
            mCursor = 0;
        }

        auto total_time = difftime(time(nullptr), start_time);
        std::cout << "Vector mCursor took " << total_time << " seconds of your life that you cannot have back." << std::endl;
    }
    // Output:
    // Vector mCursor took 0 seconds of your life that you cannot have back.


    // Vector "new" lol
    {
        time_t start_time = time(nullptr);

        for (size_t n = 0; n < iterations; ++n)
        {
            std::vector<sf::Sprite> spriteCache;
            spriteCache.resize(reserve_size); 
            spriteCache.clear();
        }

        auto total_time = difftime(time(nullptr), start_time);
        std::cout << "Vector 'new' took " << total_time << " seconds of your life that you cannot have back." << std::endl;
    }
    // Output:
    // Vector 'new' took 6 seconds of your life that you cannot have back.

    return 0;
}

vector::clear() took 6 seconds of your life that you cannot have back.

Vector mCursor took 0 seconds of your life that you cannot have back.

Vector 'new' took 6 seconds of your life that you cannot have back.

Upvotes: 0

Davis Herring
Davis Herring

Reputation: 39818

You should expect v.clear() to be faster than } {vector<T> v; v.reserve(old_size); (where the two braces usually delimit the same loop body), simply because it does a subset of the work of the latter. Both run destructors (if any) and set (the internal pointer behind) end to begin, but the latter also frees and (re)allocates memory, as well as containing more pointer assignments. (clear doesn’t free the memory, since it does not affect capacity.)

However, hopefully it doesn’t matter, especially if the vector is large and/or constructing/destroying the elements is expensive: it’s conceptually superior to limit a vector’s lifetime to the relevant loop iteration than to have it extend outside the loop until the (possibly distant) end of some enclosing block. It would be possible to do so without sacrificing performance by using a custom allocator to keep the storage alive longer, but that requires an enclosing block for the allocator anyway and is generally cumbersome.

Upvotes: 3

Related Questions