7

我试图通过将 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

诠释 2.365 纳秒

长 2.436 ns

短 8.156 ns

测试结论?

short 原始类型比 long 和 int 原始类型明显慢(3-4~ 倍)?

问题

  1. 为什么 short 原语比 int 或 long 慢得多?我希望 int 原始类型在 32 位 VM 上最快,并且 long 和 short 在时间上相等,或者 short 更快。

  2. Android手机也是这样吗?知道 Android 手机通常在 32 位环境中运行,现在越来越多的手机开始配备 64 位处理器。

4

2 回答 2

7

Java 字节码不支持对小于 int 的原始类型进行基本操作(+、-、*、/、>>、>>>、<<、%)。在指令集中根本没有为此类操作分配字节码。因此,VM 需要将 short(s) 转换为 int(s),执行操作,然后将 int 截断回 short 并将其存储在结果中。

用 javap 检查生成的字节码,看看你的 short 和 int 测试之间的区别。

VM/JIT 优化显然严重偏向于 int/long 操作,这是有道理的,因为它们是最常见的。

小于 int 的类型有其用途,但主要用于在数组中节省内存。它们不像简单的类成员那样适合(当然,当它是数据的适当类型时,您仍然会使用它们)。较小的成员甚至可能不会减小对象的大小。当前的虚拟机(再次)主要针对执行速度进行定制,因此虚拟机甚至可以将字段与本机机器字边界对齐,以牺牲内存消耗来提高访问性能。

于 2014-06-19T17:02:08.990 回答
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)。

于 2014-06-19T17:06:30.130 回答