我正在使用box2d在java中创建一个物理游戏。
我正在编写一个AI类,并希望确保我的数据尽可能高效地存储,同时考虑到内存对齐。
最微小的增加可能会产生巨大的差异,因为我实际上正在运行尽可能多的人工智能对象'直到系统变慢。程序已经在碰撞检测中使用了大量内存,因为我希望能够尽可能多地支持代理。
到目前为止我所理解的是,最小的Java类型是8字节,并且对象被填充为8的倍数。我在布尔数组中构造了我的AI控件,表示移动:x +/- 1,y +/- 1 ,以及某些固定装置的顺时针/逆时针旋转。
由于Java没有为布尔值设置空值,因此我使用bool值on_off和pos_neg嵌套了命令对象中的控件。通过移动和旋转,我每个人处理大约7个命令对象,默认情况下是'行动,(如向右移动)。所以我为每个动作创建了Command数组。
我的问题是:我这样做有效吗?
我还没有最终确定设计,所以我不确定每个阵列的大小。但是,考虑到内存对齐要求,我猜测我至少会有一些填充,这最终会浪费内存。我正在考虑做一些事情,比如切割对象大小以适应填充限制然后将来自多个对象的剩余数据推送到“溢出”状态。对象......或类似的东西。
这会加快速度吗?为什么或为什么不呢?
我也在考虑使用比特集,不过我认为我的命令对象可能已经取得了类似的结果,而且我被告知比特移位很慢。
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) //
},
};
}
答案 0 :(得分:3)
这只是一个评论,但有点冗长,我并不想把它写成3条评论。
由于这是另一个人的后续问题,我会从&#34开始;不要担心填充"。担心如何存储您的数据。
而且,如果您要担心物品占用多少空间,请分配7个对象而不是7个单独对象的数组。我确信Java在每个分配中都有开销。在典型的C或C ++实现中,new
或malloc
的每个分配占用超过所分配的实际数据大小的16-32个字节,并且大小舍入为16或32个字节。在java中有一个建议here,一个对象的内存开销是8个字节 - 对于所有Java VM和实现可能都不是这样。
此外,所有空间和时间优化都是空间和时间之间的折衷[几乎总是至少],因此以更紧凑的形式存储数据将花费时间来节省空间。例如,我可以认为在较大的整数结构中将on_off
和pos_neg
对作为两位。所以你的7个命令将存储在一个整数中。但是,现在您必须进行轮班和屏蔽才能获得正确的数据。同样,如果要存储某些东西,也可以进行移动和定位。 (我把它写成C,因为我不太了解Java)。
/* 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}
,而不是每个(可能)每个占用一个字节。但要获得每个,你必须每次做一些额外的操作(并且代码变得更加混乱)。这是否值得拥有&#34;或者不高度取决于具体情况,您访问这些的频率与系统内存的低有多大(假设THESE对象的内存使用是造成问题的原因 - 如果您有100个命令结构和40000000个对象使用命令,然后这些命令不会成为问题)。
我存储方向/移动命令的方式可能是两个整数值(如果空间紧张,则在java中int8_t
[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个单位的小盒子。这些盒子用&#34;魔术贴&#34;制成,所以它们粘在一起,可以随意拆分,但你必须跟踪哪些属于一起,每次你分开&#34;或者&#34;放在一起&#34;单位,需要额外的时间。
如果你想变得懒惰并且简单易行,你只需将三盒装的东西放在行李箱中,每个行李箱旁边都有一个空的空间。
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的更现实的单位中做同样的事情,但我认为这会让你更难阅读并且肯定会更难打字]