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

标签 java object memory-management artificial-intelligence memory-alignment

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

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

最小的增加可能会产生巨大的变化,因为我实际上正在“尽可能多地运行AI对象”,直到系统变慢为止。该程序已经在碰撞检测中使用了大量内存,因为我想再次能够支持尽可能多的代理商。

到目前为止,我了解到的是,最小的Java类型是8个字节,并且对象被填充为8的倍数。我已将我的AI控件构造为布尔数组,表示运动:x +/- 1,y +/- 1和顺时针方向/ CCW旋转用于某些灯具。

由于Java没有为布尔值设置空值,因此我将控件嵌套在具有bool值on_off和pos_neg的命令对象中。通过移动和旋转,我每个“默认”操作(例如向右移动)处理大约7个命令对象。所以我为每个动作创建命令数组。

我的问题是:我能有效地做到这一点吗?

我还没有完成设计,所以我不确定每个数组的大小。但是,考虑到内存对齐的要求,我猜我至少会有一些填充,最终会浪费内存,我正在考虑做一些事情,例如削减对象大小以适应填充限制,然后从多个内存中推送剩余数据对象变成“溢出”对象...之类的东西。

这样会加快速度吗?为什么或者为什么不?

我还考虑使用位集,尽管我认为我的命令对象可能已经达到了类似的结果,并且有人告诉我位移很慢。

public class Command {

        boolean on_off  = false;
        boolean pos_neg = false;
}

public class DefaultMoves {

    //Note: these arrays are 2d in the event multiples are necessary
    //to complete a single action, I may change this later.
    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中,建议here对象的内存开销为8个字节-这可能不适用于所有Java VM和实现。

此外,所有时空优化都是时空之间的折衷(至少几乎总是如此),因此以更紧凑的形式存储数据将节省时间,从而节省了空间。例如,我可以认为在较大的整数结构中将成对的on_offpos_neg作为两个位。因此,您的7个命令将存储在一个整数中。但是,现在您必须进行移位和屏蔽才能获取正确的数据。同样,如果要存储某些内容,请转移和操作。 (因为我不太了解Java,所以我将其编写为C)。

/* This is big enough for 16 pairs of on_off and pos_neg */
/* In a pair of bits, bit 0 = on_off, bit 1 = pos_neg */
uint32_t cmdData;

/* Get values of on_off and pos_neg for element number n */
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;
}

/* Store values for element 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; /* Clear bits */
    cmdData |= bits << (n * 2);
}


如您所见,这可以更有效地存储数据,因为我们可以在4个字节中存储16对{on_off, pos_neg},而不是每个(可能)占用一个字节。但是,要深入了解它们,您每次都必须做一些额外的操作(并且代码变得更加混乱)。是否“值得拥有”在很大程度上取决于情况,访问这些对象的频率与系统内存的低低(假设这些对象的内存使用量是造成问题的原因-如果您有100个命令结构,并且使用该命令的40000000个对象,那么这些命令就不会成为问题。

我存储方向/运动命令的方式可能是两个整数值(如果空间狭窄,则为int8_t [java中的byte]),例如,按住+1进行向右或向下移动,-1向左或向上移动。这不是最紧凑的形式,但是它可以轻松访问和轻松计算新职位。

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

struct direction
{
     int x, y;
};

direction directions[] =
{
     { 0, 0 },    // Don't move.
     { 0, 1 },    // Right.
     { 0, -1 },   // Left.
     { 1, 0 },    // Down.
     { -1, 0 },    // Up.
 };


如果您也想对角移动,则必须使用{ 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个单位的盒子,然后再切下一个,再将其粘贴在第一个旁边,然后将剩余的两个单位放在下一个空间,然后切成两个单位从下一个开始,并将其粘贴到数据包2旁边,依此类推。

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


现在,您已使用了25%的空间来存储它们,但是您花了一些时间将它们拆分,并且当您以后需要使用数据时,您不得不花时间再次将它们分成三个单元。

现在,想像一下您可以付钱将东西放入手提箱,并且按放置的物品获得报酬,您选择哪种方法?

然后,您不必为每件物品付费,而必须为行李箱空间付费(因为您是公司的所有者)。现在,您想尽可能地挤入空间。但这需要额外的时间,对吗?如果手提箱很贵,那也许值得。如果不是那么贵,您可能更希望节省时间而不是空间。这是一个妥协。

[我们可以以更实际的单位8、32或64进行相同的操作,但是我认为那只会使阅读变得更困难,并且肯定使打字变得更加困难]

关于java - 物理游戏的内存高效AI对象,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33829894/

相关文章:

java - 为单个和多个对象创建对象容器

javascript - 定义了一个可以作为对象调用的函数

angular - 从 angular 2 中的对象访问特定值

java - 我们如何知道Integer类对象的大小?

c++ - 为什么 C++ 标准库容器使用内存池,如果显然 malloc/free 对做同样的工作?

java - RabbitMQ + Reactor,传递消息后发送ACK?

java.lang.NoClassDefFoundError : groovy/lang/GroovyObject 错误

java - 队列没有产生正确的输出

c++ - 使用 C++ 在 Arduino 上编写复杂的程序。如何正确使用对象或更好地使用普通 C?

iphone - 在 ios 中释放内存