一种缓存友好的方式来存储线程

时间:2017-06-12 08:32:52

标签: c++ multithreading

前段时间我编写了一个2D图形和物理引擎来进行游戏,现在我回过头来给它一些性能优化(仅用于训练)。

目前我正在考虑对存储和处理数据的方式进行一些优化。

基本上,我正在考虑三种方法,并希望对它们有一些建议(或者甚至是更好的主意)。

存储的数据通过多个线程非常频繁地访问。

我做了一个非常简单(不工作!)的代码示例来说明我的三种方法的执行是如何工作的。

这些是我用于此示例的类和结构:

发生碰撞的方向的枚举

enum direction
{
    up,
    down,
    left, 
    right,
    none //No collision
};

一个存储物理属性的类,它具有计算速度等简单的逻辑。

class PhysicProperties
{
public:
    void calculateSpeedFromCollision(direction dir)
    {
        //Threadsafe!

        //Some calculations
    }

private:
    float m_Velocity;
    float m_Acceleration;
    float m_Mass;

    //Some other stuff
};

一个存储碰撞属性的类,它可以确定两个类是否相互碰撞

class CollisionProperties
{
public:
    void checkCollision(Actor& actor)
    {
        bool collision = false;

        //check collision

        if(!collision)
        {
            m_CollisionDir = none;
        }
    }

    direction getDirOfLastCollision()
    {
        //Threadsafe!

        return m_CollisionDir;
    }

private:
    direction                         m_CollisionDir;
    std::vector<std::pair<int, int> > m_CollisionBody;

    //Some other stuff
};

包含Physic / CollisionProperties的actor类,以及GraphicProperties之类的东西等。

class Actor
{
public:
    void processCollision()
    {
        //Threadsafe!

        /*This function takes somewhat between 3 and 5 times the num of cpu cycles I need to 
        create a task + add task to pool + pool latency*/

        direction dir = m_Collision.getDirOfLastCollision(); //if no collision happened, dir = none

        if (dir != none)
        {
            m_Physics.calculateSpeedFromCollision(dir);
        }
    }

private:
    PhysicProperties    m_Physics;
    CollisionProperties m_Collision;
};

好了,我正在考虑的3种方法:

在第一个方法中,Actors存储在一个向量中,每个物理计算有一个任务:

void doPhysic()
{   
    std::for_each(actors.begin(), actors.end(), [](Actor& actor)->void {m_Pool.run(actor, actor.processCollision); });
} 

在第二种方法中,数据存储在一个向量中,但向量在n个线程之间分割。此示例非常静态(为简单起见)。我当然会把它编码得更加动态。

void doPhysic()
{   
    std::vector<Actor>::iterator begin1; // begin
    std::vector<Actor>::iterator end1;   // 1/4

    std::vector<Actor>::iterator begin2; // 1/4
    std::vector<Actor>::iterator end2;   // 2/4

    std::vector<Actor>::iterator begin3; // 2/4
    std::vector<Actor>::iterator end3;   // 3/4

    std::vector<Actor>::iterator begin4; // 3/4
    std::vector<Actor>::iterator end4;   // 4/4

    m_Pool.run(std::bind(std::for_each<std::vector<Actor>::iterator, void(*)(Actor&)>, begin1, end1, [](Actor& actor)->void { actor.processCollision(); }));
    m_Pool.run(std::bind(std::for_each<std::vector<Actor>::iterator, void(*)(Actor&)>, begin2, end2, [](Actor& actor)->void { actor.processCollision(); }));
    m_Pool.run(std::bind(std::for_each<std::vector<Actor>::iterator, void(*)(Actor&)>, begin3, end3, [](Actor& actor)->void { actor.processCollision(); }));

    std::for_each(begin4, end4, [](Actor& actor)->void { actor.processCollision(); });

    //Wait until 1-3 is done
}

在第三种方法中,我将数据存储在n个向量中(每个线程一个,并以有意义的方式排序)。

void doPhysic()
{   
    m_Pool.run(std::bind(std::for_each<std::vector<Actor>::iterator, void(*)(Actor&)>, actors1.begin(), actors1.end(), [](Actor& actor)->void { actor.processCollision(); }));
    m_Pool.run(std::bind(std::for_each<std::vector<Actor>::iterator, void(*)(Actor&)>, actors2.begin(), actors2.end(), [](Actor& actor)->void { actor.processCollision(); }));
    m_Pool.run(std::bind(std::for_each<std::vector<Actor>::iterator, void(*)(Actor&)>, actors3.begin(), actors3.end(), [](Actor& actor)->void { actor.processCollision(); }));

    std::for_each(actors4.begin(), actors4.end(), [](Actor& actor)->void { actor.processCollision(); });

    //Wait until 1-3 is done
}

我可以想象,3是最友好的缓存方式,因为不同的线程可以为自己预取数据并且不必共享很多,而且矢量不会变大,矢量甚至可能适合缓存作为一个整体。另一方面,如果我试图找到一个特定的演员,我会失去一些表现。

方法1不是非常缓存友好的,因为核心不应该能够预测下一个哪个actor,这意味着预取不会对此有效。

方法2应该是1和2之间的良好折衷,实际上是我赞成的。

0 个答案:

没有答案