std::向量-游戏循环中的优化

std::vector - optimization during game loop

本文关键字:优化 循环 游戏 向量 std      更新时间:2023-10-16

我只是对设计的优化有一些问题。

游戏中的所有游戏对象都继承自基类

class Environment;

游戏在向量上迭代,更新并渲染每个对象:

for (auto& env : this->listEnvironment)
{
    if (env->GetIsMarkedForDeletion()==false)
    {
        env->Update();
        env->Render();
    }
}

只要对象没有被标记为删除即可。

所以这就是我想知道的,是创建一个单独的循环并从向量中删除所有标记为删除的对象更好,只是让它们留在向量中,不渲染它们,还是应该在渲染的同一个循环中进行?

据我所知,如果我在循环中调整向量的大小,性能会下降很多,但我可能误解了这一点。

这里有一个方法,在保持环境列表清洁方面几乎没有开销:

编辑2:可能和它的效率一样高。感谢评论中的灵感:

struct Environment {
    virtual bool GetIsMarkedForDeletion() const;
    virtual void Render() const;
    virtual void Update();
};
struct World {
    using environment_container = std::vector<std::unique_ptr<Environment>>;
    environment_container listEnvironment;
    static bool is_removable(const environment_container::value_type& ptr)
    {
        return ptr->GetIsMarkedForDeletion();
    }
    void do_update_and_render()
    {
        listEnvironment.erase(std::remove_if(begin(listEnvironment),
                                        end(listEnvironment),
                                        is_removable),
                              end(listEnvironment));
        for (auto& env : this->listEnvironment)
        {
                env->Update();
                env->Render();
        }
    }
};

编辑:为了回应AlchemicalApples对内存碎片的担忧,提供了版本2,除非环境的大小超过其高水位线,否则不会释放内存:

struct Environment {
    virtual bool GetIsMarkedForDeletion() const;
    virtual void Render() const;
    virtual void Update();
};
struct World {
    using environment_container = std::vector<std::unique_ptr<Environment>>;
    environment_container listEnvironment;
    environment_container survivingEnvironment; // = {}
    void do_update_and_render()
    {
        if (survivingEnvironment.capacity() < listEnvironment.size()) {
            survivingEnvironment.reserve(listEnvironment.size());
        }
        for (auto& env : this->listEnvironment)
        {
            if (env->GetIsMarkedForDeletion()==false)
            {
                env->Update();
                env->Render();
                survivingEnvironment.push_back(move(env));
            }
        }
        survivingEnvironment.swap(listEnvironment);
        survivingEnvironment.clear();   // note-does not clear memory so fragmentation is prevented
    }
};

原件在这里进行比较:

struct Environment {
    virtual bool GetIsMarkedForDeletion() const;
    virtual void Render() const;
    virtual void Update();
};
struct World {
    using environment_container = std::vector<std::unique_ptr<Environment>>;
    environment_container listEnvironment;
    void do_update_and_render()
    {
        environment_container new_objects;
        new_objects.reserve(listEnvironment.size());
        for (auto& env : this->listEnvironment)
        {
            if (env->GetIsMarkedForDeletion()==false)
            {
                env->Update();
                env->Render();
                new_objects.push_back(move(env));
            }
        }
        swap(new_objects, listEnvironment);
    }
};

在没有分配潜在的大型新矢量的情况下进行更新的版本:

void do_update_and_render_in_place()
{
    auto cursor = this->listEnvironment.begin();
    auto sentry = this->listEnvironment.end();
    while(sentry != cursor)
    {
        auto &element = **cursor;
        if(element.GetIsMarkedForDeletion()) { break; }
        element.Update();
        element.Render();
        ++cursor;
    }
    if(sentry == cursor) { return; }
    auto trailing = cursor; // beginning of deleted elements
    ++cursor;
    for(; sentry != cursor; ++cursor) {
        auto &element = **cursor;
        if(false == element.GetIsMarkedForDeletion()) { continue; }
        element.Update();
        element.Render();
        swap(*cursor, *trailing);
        ++trailing;
    }
    this->listEnvironment.erase(trailing, sentry);
}