我尝试通过将int基元更改为short来优化Android游戏的RAM使用率。在我这样做之前,我对Java中原始类型的性能感兴趣。
所以我使用caliper库创建了这个小测试基准。
public class BenchmarkTypes extends Benchmark {
@Param("10") private long testLong;
@Param("10") private int testInt;
@Param("10") private short testShort;
@Param("5000") private long resultLong = 5000;
@Param("5000") private int resultInt = 5000;
@Param("5000") private short resultShort = 5000;
@Override
protected void setUp() throws Exception {
Random rand = new Random();
testShort = (short) rand.nextInt(1000);
testInt = (int) testShort;
testLong = (long) testShort;
}
public long timeLong(int reps){
for(int i = 0; i < reps; i++){
resultLong += testLong;
resultLong -= testLong;
}
return resultLong;
}
public int timeInt(int reps){
for(int i = 0; i < reps; i++){
resultInt += testInt;
resultInt -= testInt;
}
return resultInt;
}
public short timeShort(int reps){
for(int i = 0; i < reps; i++){
resultShort += testShort;
resultShort -= testShort;
}
return resultShort;
}
}
测试结果让我感到惊讶。
测试环境
基准测试在Caliper库下运行。
测试结果
https://microbenchmarks.appspot.com/runs/0c9bd212-feeb-4f8f-896c-e027b85dfe3b
Int 2.365 ns
长2.436 ns
短8.156 ns
测试结论?
短基元类型比long和int基元类型慢得多(3-4次)?
问题
为什么短原语明显慢于int或long?我希望int原语类型在32位虚拟机上最快,长短在时间上相等,或短到更快。
Android手机也是如此吗?知道Android手机通常在32位环境下运行,现在越来越多的手机开始配备64位处理器。
答案 0 :(得分:6)
Java字节代码不支持基本操作(+, - ,*,/,&gt;&gt;,&gt;&gt;&gt;,&lt;&lt;,&lt;,%)对原始类型较小比int。在指令集中没有为这种操作分配字节代码。因此,VM需要将short(s)转换为int(s),执行操作,然后将int截断为short并将其存储在结果中。
使用javap检查生成的字节代码,以查看short和int测试之间的区别。
VM / JIT优化显然偏向于int / long操作,这是有道理的,因为它们是最常见的。
小于int的类型有它们的用途,但主要用于在数组中保存内存。它们不像简单的类成员那样适合(当然,当它适用于数据的类型时,你仍然会使用它们)。较小的成员可能甚至不会减小对象大小。当前VM(再次)主要针对执行速度进行了定制,因此VM甚至可以将字段与本机机器字边界对齐,从而以牺牲内存花费为代价来提高访问性能。
答案 1 :(得分:3)
由于java / android处理小于int的基元的整数算术,这是可能的。
如果在java中添加了两个原语,这些原语的数据类型小于int,则会自动将它们提升为整数数据类型。通常需要强制转换才能将结果转换回必要的数据类型。
诀窍来自速记操作,如+=
,-=
等等,其中强制转换隐式,以便操作的最终结果:
resultShort += testShort;
实际上类似于:
resultShort = (short)((int) resultShort + (int) testShort);
如果我们查看方法的反汇编字节码:
public static int test(int a, int b){
a += b;
return a;
}
我们看到了:
public static int test(int, int);
Code:
0: iload_0
1: iload_1
2: iadd
3: istore_0
4: iload_0
5: ireturn
将此与数据类型的相同方法进行比较,我们得到:
public static short test(short, short);
Code:
0: iload_0
1: iload_1
2: iadd
3: i2s
4: istore_0
5: iload_0
6: ireturn
注意附加指令i2s
(整数到短)。这可能是性能损失的罪魁祸首。您可以注意到的另一件事是所有指令都是基于整数的,由前缀i
表示(例如iadd
表示整数加)。这意味着在iload
阶段的某个地方,短路被提升为整数,这可能也会导致性能下降。
如果您可以接受我的话,长算术的字节码与整数的字节码相同,但指令是长特定的(例如ladd
而不是iadd
)。