Java 为什么短基元类型明显比长基元类型或int基元类型慢?

Java 为什么短基元类型明显比长基元类型或int基元类型慢?,java,android,performance,caliper,Java,Android,Performance,Caliper,我试图通过将int原语改为short来优化Android游戏的RAM使用。在此之前,我对Java中基本类型的性能感兴趣 所以我用卡钳库创建了这个小测试基准 public class BenchmarkTypes extends Benchmark { @Param("10") private long testLong; @Param("10") private int testInt; @Param("10") private short testShort;

我试图通过将int原语改为short来优化Android游戏的RAM使用。在此之前,我对Java中基本类型的性能感兴趣

所以我用卡钳库创建了这个小测试基准

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;
    }
}
public类BenchmarkTypes扩展了Benchmark{
@参数(“10”)专用长测试长;
@参数(“10”)私人内部测试;
@参数(“10”)专用短测试短;
@参数(“5000”)专用长结果长度=5000;
@参数(“5000”)私有int resultInt=5000;
@参数(“5000”)私有短结果短=5000;
@凌驾
受保护的void setUp()引发异常{
Random rand=新的Random();
testShort=(short)rand.nextInt(1000);
testInt=(int)testShort;
testLong=(long)testShort;
}
公共长时间(int代表){
对于(int i=0;i
测试结果令我惊讶

测试环境

在卡尺库下运行基准测试

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;
    }
}
测试结果

整数2.365纳秒

长2.436纳秒

短8.156纳秒

测试结论?

短基元类型比长基元和int基元类型慢很多(3-4倍)

问题

  • 为什么短原语明显比int或long慢?我希望int原语类型在32位VM上是最快的,long和short在时间上是相等的,short甚至更快

  • Android手机也是这样吗?了解到Android手机一般在32位环境下运行,现在越来越多的手机开始配备64位处理器


  • Java字节码不支持基本运算(+、-、*、/、>>、>>>,,这是可能的,因为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
    )。

    你没有对JIT进行预热。你没有做足够的迭代。这不是你使用微Bench Java的方式。很可能是这样的由Java将short(每次)转换为int(或long)引起算术operations@GermannArlington-不。对1000倍计时差异的真正解释是基准的编写不正确。请参阅链接的Q&A。没错,这里没有真正的基准!但是速度慢了1000倍?创建一个好的基准真的会产生如此大的差异吗?使用t更新了问题的新测试结果这个答案是正确的。但是,重要的是要记住JVM不会直接执行字节码(一旦它被JIT编译)。因此字节码并不能直接解释差异。例如,如果本机指令集直接支持16算术,并且JIT编译器足够聪明地使用它,那么您可能会期望
    short
    算术比
    long
    算术更快。但实际情况是,用于PC、服务器的指令集甚至智能手机设备也被调整为32位/64位操作,而不是16位操作。因此,执行16位算术通常需要更多的本机指令和更多的时钟周期,这使得它比32位和64位慢…正如OP的基准测试所示。但这高度依赖于目标平台硬件…和可能在JIT编译器上运行。