物理游戏中内存高效的AI对象

我正在使用Box2D在Java中创建一个物理游戏。

我正在编写一个AI类,并希望确保我的数据存储尽可能高效,同时考虑到内存对齐。

哪怕是最小的增加也可能会产生巨大的差异,因为我实际上是在运行’尽可能多的AI对象’,直到系统变慢。程序已经在碰撞检测上使用了大量的内存,因为我再次希望能够支持尽可能多的代理。

据我所知,Java中最小的类型是8字节,并且对象会被填充到8的倍数。我已经将我的AI控制结构化为布尔数组,代表移动:x+/-1,y+/-1,以及某些固定装置的顺时针/逆时针旋转。

由于Java没有布尔值的null设置,我将控制嵌套在命令对象中,布尔值为on_off和pos_neg。对于移动和旋转,我处理的是每个’默认’动作大约7个命令对象,(例如向右移动)。所以我为每个动作创建了命令数组。

我的问题是:我这样做是否高效?

我还没有最终确定设计,所以我不确定每个数组的大小。但是,考虑到内存对齐要求,我猜测我至少会有一些填充,这最终是浪费的内存。我正在考虑做一些事情,比如将对象大小调整到填充限制,然后将多个对象的剩余数据推入一个’溢出’对象…或者类似的东西。

这会加速吗?为什么会或不会?

我也在考虑使用位集,尽管我认为我的命令对象可能已经达到了类似的结果,而且我被告知位移操作很慢。

public class Command {        boolean on_off  = false;        boolean pos_neg = false;}public class DefaultMoves {    //注意:这些数组是2D的,以防需要多个来完成一个动作,我可能会稍后更改。    Command[][] mvRight =            {               {     new Command(false, false), //                    new Command(false, false), //                    new Command(false, false), //                    new Command(false, false), //                    new Command(false, false), //                    new Command(true, true), //moveX                    new Command(false, false)  //                 },           };    Command[][] mvLeft =            {               {     new Command(false, false), //                    new Command(false, false), //                    new Command(false, false), //                    new Command(false, false), //                    new Command(false, false), //                    new Command(true, false), //moveX                    new Command(false, false)  //                 },           };}

回答:

这本来只是一个评论,但有点长了,我不想把它写成3个评论。

由于这是另一个问题的后续问题,我会建议“不要担心填充”。你应该关心的是如何存储你的数据。

而且,如果你要担心你的东西占用了多少空间,不如分配一个包含7个对象的数组,而不是7个独立的对象。我相信Java在每次分配时都有开销。在典型的C或C++实现中,每次使用newmalloc进行分配时,会占用16-32字节的额外空间,并且大小会四舍五入到16或32字节。在Java中,这里有一个建议这里表明对象的内存开销是8字节 – 这可能并不适用于所有Java虚拟机和实现。

此外,所有的空间和时间优化几乎总是在空间和时间之间进行妥协,因此以更紧凑的形式存储数据将会在时间上付出代价,以节省空间。例如,我可以想到将on_offpos_neg的对作为一个较大整数结构中的两个位来存储。所以你的7个命令将存储在一个整数中。然而,现在你必须进行移位和掩码操作才能获取正确的数据。同样,如果你要存储一些东西,你需要进行移位和或操作。(我用C语言写这个,因为我不太熟悉Java)。

/* 这足以容纳16对on_off和pos_neg *//* 在一对位中,位0 = on_off,位1 = pos_neg */uint32_t cmdData;/* 获取元素编号n的on_off和pos_neg的值 */void getData(int n, bool& on_off, bool& pos_neg){    uint32_t shifted = cmdData >> (2 * n);    on_off = (shifted & 1) != 0;    pos_neg = (shifted & 2) != 0;}/* 存储元素n的值 */void setData(int n, bool on_off, bool pos_neg){    uint32_t bits = (int)on_off + (2 * (int)pos_neg);     uint32_t mask = 3 << (n * 2);    cmdData &= ~mask; /* 清除位 */    cmdData |= bits << (n * 2);}

如你所见,这更有效地存储了数据,因为我们可以在4字节中存储16对{on_off, pos_neg},而不是每个(可能)占用一个字节。但每次获取数据时,你必须进行一些额外的操作(而且代码会变得更混乱)。这是否“值得拥有”高度依赖于具体情况,你访问这些数据的频率与系统的内存使用情况(假设这些对象的内存使用是导致问题的根源 – 如果你有100个命令结构和40000000个使用这些命令的对象,那么命令就不是问题所在)。

我存储方向/移动命令的方式可能是两个整数值(如果空间紧张,则使用int8_t [byte 在Java中]),例如,移动右或下时为+1,移动左或上时为-1。这不是最紧凑的形式,但它便于访问和计算新位置。

然后可以使用这对值来描述所有可能的方向:

struct direction{     int x, y;};direction directions[] ={     { 0, 0 },    // 不移动。     { 0, 1 },    // 右。     { 0, -1 },   // 左。     { 1, 0 },    // 下。     { -1, 0 },    // 上。 };

如果你还想斜向移动,你需要再添加四个组合{ 1, 1 }, {-1, 1}等。

同样的方法可以应用于可以移动的对象,作为一对xDir, yDir值。

但关键在于,你首先需要很好地理解什么更重要:空间还是计算。从中找出哪些对象占用了大部分空间(哪些数量最多)。调整你只有一个或几十个的对象大小不会有太大不同,而你有数百万个的对象则会。如果空间不是问题(说实话,在一个拥有千兆字节RAM的系统中,很难编写出有效使用大量数据的代码 – 如果你对每个对象每帧都做一些事情,通常是CPU先耗尽速度,而不是内存耗尽)。

手提箱类比:

想象你有一个手提箱,它的宽度刚好可以容纳4个小盒子(长度可以是任意数量 – 这是一个奇怪的手提箱!),你有更大的盒子,它们是1、2、3或4个小盒子的单位。这些盒子是用“魔术贴”做的,所以它们可以粘在一起,并且可以按你喜欢的方式分开,但你必须跟踪哪些属于一起,每次你“分开”或“重新组合”这些单位时,都需要额外的时间。

如果你想偷懒并简化操作,你只需将你的三盒单位放入手提箱,每个旁边留一个空位。

 1 2 3 4 a a a x b b b x c c c x d d d x 

依此类推。

如果你想紧密打包,你可以拿一个3单位的盒子,然后切下一单位的下一个盒子,放在第一个旁边,然后将剩余的两单位放在下一个空间,然后从下一个盒子中切下一块两单位的,放在第二个包旁边,依此类推。

1 2 3 4a a a bb b c cc d d d 

现在,你使用了25%的空间来存储它们,但你花时间分开它们,并且你需要花时间再次将它们取出成三单位的形式,当你稍后需要使用这些数据时。

现在,想象你因为放东西进手提箱而获得报酬,你按放入的物品数量获得报酬,你会选择哪种方法?

然后考虑到你不是按物品数量获得报酬,而是要为手提箱空间付费(因为你是公司的主人)。现在你想尽可能多地塞进空间。但这需要额外的时间,对吗?如果手提箱空间昂贵,那么这可能是值得的。如果不是那么昂贵,你可能更愿意节省时间而不是空间。这是一个妥协。

[我们可以用更现实的8、32或64单位做同样的事情,但我认为这只会使它更难读,并且肯定会使它更难输入]

Related Posts

L1-L2正则化的不同系数

我想对网络的权重同时应用L1和L2正则化。然而,我找不…

使用scikit-learn的无监督方法将列表分类成不同组别,有没有办法?

我有一系列实例,每个实例都有一份列表,代表它所遵循的不…

f1_score metric in lightgbm

我想使用自定义指标f1_score来训练一个lgb模型…

通过相关系数矩阵进行特征选择

我在测试不同的算法时,如逻辑回归、高斯朴素贝叶斯、随机…

可以将机器学习库用于流式输入和输出吗?

已关闭。此问题需要更加聚焦。目前不接受回答。 想要改进…

在TensorFlow中,queue.dequeue_up_to()方法的用途是什么?

我对这个方法感到非常困惑,特别是当我发现这个令人费解的…

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注