【问题标题】:Why is the short primitive type significantly slower than long or int?为什么 short 基本类型比 long 或 int 慢得多?
【发布时间】:2014-06-19 10:31:18
【问题描述】:

我尝试通过将 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 纳秒

8.156 ns 短

测试结论?

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

问题

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

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

【问题讨论】:

  • 您还没有预热 JIT。你没有做足够的迭代。这不是您对 Java 进行微基准测试的方式。
  • 它(很可能)是由 Java 将 short(每次)转换为 int(或 long)以进行算术运算
  • @GermannArlington - 不。对 1000 倍时间差异的真正解释是基准编写不正确。请参阅链接的问答。
  • 没错,这里没有真正的基准!但是慢 1000 倍?创建一个好的基准真的会有这么大的不同吗?
  • 使用 java caliper 库用新的测试结果更新了问题。

标签: java android performance caliper


【解决方案1】:

Java 字节码不支持对小于 int 的基本类型的基本操作(+、-、*、/、>>、>>>、

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

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

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

【讨论】:

    【解决方案2】:

    这可能是由于 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)。

    【讨论】:

    • 这个答案是正确的。但是,重要的是要记住 JVM 不直接执行字节码(一旦它被 JIT 编译)。因此,字节码不能直接解释差异。例如,如果本机指令集直接支持 16 算术,并且 JIT 编译器足够智能以使用它,那么您可能会认为 short 算术比 long 算术更快。
    • 但现实情况是,PC、服务器甚至智能手机设备的指令集都针对 32 位/64 位操作而不是 16 位操作进行了调整。因此,执行 16 位算术通常需要更多的 native 指令和更多的时钟周期,这使得它比 32 位和 64 位要慢……正如 OP 的基准测试所示。但这高度依赖于目标平台硬件......并且可能依赖于 JIT 编译器。
    猜你喜欢
    • 1970-01-01
    • 2013-11-19
    • 1970-01-01
    • 1970-01-01
    • 2022-09-28
    • 2020-04-08
    • 2017-05-29
    • 2019-12-12
    • 1970-01-01
    相关资源
    最近更新 更多