在java中,使用byte或short而不是int和float而不是double更有效吗?

Dis*_*ron 87 java double int performance primitive-types

我注意到我总是使用int和double,不管数量需要多大或多小.所以在java中,使用byteshort代替intfloat不是代替double

所以假设我有一个有大量整数和双打的程序.如果我知道这个数字适合的话,是否值得通过并将我的int更改为字节或短裤?

我知道java没有无符号类型,但如果我知道这个数字只是正数,那么还有什么我可以做的吗?

通过有效我主要是指处理.如果所有变量都是一半大小并且计算可能会稍快一些,我认为垃圾收集器会快得多.(我想因为我正在开发android,所以我也需要担心ram)

(我假设垃圾收集器只处理对象而不是原始但仍然删除被遗弃对象中的所有原语吗?)

我尝试了一个小的Android应用程序,但我没有真正注意到差异.(虽然我没有"科学地"衡量任何东西.)

假设它应该更快更有效,我错了吗?我讨厌在一个庞大的计划中改变一切,发现我浪费了我的时间.

当我开始一个新项目时,从一开始就值得做吗?(我的意思是我认为每一点都会有所帮助,但如果是这样的话,为什么不像任何人那样做.)

Ste*_*n C 101

假设它应该更快更有效,我错了吗?我讨厌在一个庞大的计划中改变一切,发现我浪费了我的时间.

简短的回答

是的,你错了.在大多数情况下,它在使用的空间方面几乎没有差别.

这是不值得尝试,除非你有明确的证据表明需要优化来优化这个....如果您确实需要优化对象字段的内存使用,您可能需要采取其他(更有效)的措施.

更长的答案

Java虚拟机使用(实际上)32位原始单元大小的倍数的偏移来建模堆栈和对象字段.因此,当您将局部变量或对象字段声明为(例如)a时byte,变量/字段将存储在32位单元格中,就像一个int.

这有两个例外:

  • longdouble值需要2个原始32位单元
  • 基本类型的数组以压缩形式表示,因此(例如)一个字节数组每32位字保存4个字节.

因此,可能值得优化使用longdouble...以及大型基元数组.但总的来说没有.

从理论上讲,JIT 可能能够对此进行优化,但实际上我从来没有听说过JIT.一个障碍是JIT通常直到创建了正在编译的类的实例之后才能运行.如果JIT优化了内存布局,那么你可能会有两个(或更多)"同类"对象的"风味"......这会带来很大的困难.


再访

看看@ meriton的答案中的基准测试结果,似乎使用shortbyte不是int对乘法产生性能损失.实际上,如果你单独考虑这些操作,那么惩罚就很重要.(你不应该......但那是另一回事.)

我认为解释是JIT可能在每种情况下使用32位乘法指令进行乘法运算.但在byteshort情况下,执行额外的指令以中间的32位值转换为一个byteshort在每次循环迭代.(理论上,转换可以在循环结束时完成一次......但我怀疑优化器是否能够解决这个问题.)

无论如何,这确实指向切换到shortbyte作为优化的另一个问题.它会使性能变差 ......在算法和计算密集的算法中.

  • 除非您有明确的性能问题证据,否则+1不会优化 (28认同)

mer*_*ike 28

这取决于JVM的实现以及底层硬件.大多数现代硬件不会从内存(甚至从第一级缓存)获取单个字节,即使用较小的基元类型通常不会减少内存带宽消耗.同样,现代CPU的字大小为64位.它们可以在较少的位上执行操作,但是通过丢弃额外的位来工作,这也不是更快.

唯一的好处是较小的基元类型可以导致更紧凑的内存布局,最明显的是在使用数组时.这节省了内存,这可以改善引用的局部性(从而减少缓存未命中数)并减少垃圾收集开销.

然而,一般而言,使用较小的基元类型并不快.

为证明这一点,请看以下基准:

package tools.bench;

import java.math.BigDecimal;

public abstract class Benchmark {

    final String name;

    public Benchmark(String name) {
        this.name = name;
    }

    abstract int run(int iterations) throws Throwable;

    private BigDecimal time() {
        try {
            int nextI = 1;
            int i;
            long duration;
            do {
                i = nextI;
                long start = System.nanoTime();
                run(i);
                duration = System.nanoTime() - start;
                nextI = (i << 1) | 1; 
            } while (duration < 100000000 && nextI > 0);
            return new BigDecimal((duration) * 1000 / i).movePointLeft(3);
        } catch (Throwable e) {
            throw new RuntimeException(e);
        }
    }   

    @Override
    public String toString() {
        return name + "\t" + time() + " ns";
    }

    public static void main(String[] args) throws Exception {
        Benchmark[] benchmarks = {
            new Benchmark("int multiplication") {
                @Override int run(int iterations) throws Throwable {
                    int x = 1;
                    for (int i = 0; i < iterations; i++) {
                        x *= 3;
                    }
                    return x;
                }
            },
            new Benchmark("short multiplication") {                   
                @Override int run(int iterations) throws Throwable {
                    short x = 0;
                    for (int i = 0; i < iterations; i++) {
                        x *= 3;
                    }
                    return x;
                }
            },
            new Benchmark("byte multiplication") {                   
                @Override int run(int iterations) throws Throwable {
                    byte x = 0;
                    for (int i = 0; i < iterations; i++) {
                        x *= 3;
                    }
                    return x;
                }
            },
            new Benchmark("int[] traversal") {                   
                @Override int run(int iterations) throws Throwable {
                    int[] x = new int[iterations];
                    for (int i = 0; i < iterations; i++) {
                        x[i] = i;
                    }
                    return x[x[0]];
                }
            },
            new Benchmark("short[] traversal") {                   
                @Override int run(int iterations) throws Throwable {
                    short[] x = new short[iterations];
                    for (int i = 0; i < iterations; i++) {
                        x[i] = (short) i;
                    }
                    return x[x[0]];
                }
            },
            new Benchmark("byte[] traversal") {                   
                @Override int run(int iterations) throws Throwable {
                    byte[] x = new byte[iterations];
                    for (int i = 0; i < iterations; i++) {
                        x[i] = (byte) i;
                    }
                    return x[x[0]];
                }
            },
        };
        for (Benchmark bm : benchmarks) {
            System.out.println(bm);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

在我有点旧笔记本上打印:

int multiplication  1.530 ns
short multiplication    2.105 ns
byte multiplication 2.483 ns
int[] traversal 5.347 ns
short[] traversal   4.760 ns
byte[] traversal    2.064 ns
Run Code Online (Sandbox Code Playgroud)

如您所见,性能差异非常小.优化算法远比原始类型的选择重要.

  • 而不是说"最值得注意的是在使用数组时",我认为当存储在足够重要的数组中时,"short"和"byte"更有效率可能更简单(数组越大,效率越高)差异;`byte [2]`可能比`int [2]`效率更高或更低,但不足以解决任何问题),但单个值更有效地存储为`int`. (2认同)
  • 我检查的内容:那些基准测试总是使用int('3')作为因子或赋值操作数(循环变量,然后是变换的).我做的是使用类型因子/赋值操作数取决于左值类型:int mult 76.481 ns int mult(typed)72.581 ns short mult 87.908 ns short mult(typed)90.772 ns byte mult 87.859 ns byte mult(typed)89.524 ns int [] trav 88.905 ns int [] trav(typed)89.126 ns short [] trav 10.563 ns short [] trav(typed)10.039 ns byte [] trav 8.356 ns byte [] trav(typed)8.338 ns我想有一个很多不必要的铸造.那些测试是在android选项卡上运行的. (2认同)

WVr*_*ock 6

如果您大量使用它们,使用byte代替int可以提高性能。这是一个实验:

import java.lang.management.*;

public class SpeedTest {

/** Get CPU time in nanoseconds. */
public static long getCpuTime() {
    ThreadMXBean bean = ManagementFactory.getThreadMXBean();
    return bean.isCurrentThreadCpuTimeSupported() ? bean
            .getCurrentThreadCpuTime() : 0L;
}

public static void main(String[] args) {
    long durationTotal = 0;
    int numberOfTests=0;

    for (int j = 1; j < 51; j++) {
        long beforeTask = getCpuTime();
        // MEASURES THIS AREA------------------------------------------
        long x = 20000000;// 20 millions
        for (long i = 0; i < x; i++) {
                           TestClass s = new TestClass(); 

        }
        // MEASURES THIS AREA------------------------------------------
        long duration = getCpuTime() - beforeTask;
        System.out.println("TEST " + j + ": duration = " + duration + "ns = "
                + (int) duration / 1000000);
        durationTotal += duration;
        numberOfTests++;
    }
    double average = durationTotal/numberOfTests;
    System.out.println("-----------------------------------");
    System.out.println("Average Duration = " + average + " ns = "
            + (int)average / 1000000 +" ms (Approximately)");


}
Run Code Online (Sandbox Code Playgroud)

}

此类测试创建新TestClass. 每个测试进行 2000 万次,有 50 个测试。

这是测试类:

 public class TestClass {
     int a1= 5;
     int a2= 5; 
     int a3= 5;
     int a4= 5; 
     int a5= 5;
     int a6= 5; 
     int a7= 5;
     int a8= 5; 
     int a9= 5;
     int a10= 5; 
     int a11= 5;
     int a12=5; 
     int a13= 5;
     int a14= 5; 
 }
Run Code Online (Sandbox Code Playgroud)

我跑了这SpeedTest门课,最后得到了这个:

 Average Duration = 8.9625E8 ns = 896 ms (Approximately)
Run Code Online (Sandbox Code Playgroud)

现在我正在将 TestClass 中的整数更改为字节并再次运行它。结果如下:

 Average Duration = 6.94375E8 ns = 694 ms (Approximately)
Run Code Online (Sandbox Code Playgroud)

我相信这个实验表明,如果您正在实例化大量变量,使用 byte 而不是 int 可以提高效率

  • 请注意,此基准仅测量与分配和构建相关的成本,并且仅针对具有大量单个字段的类的情况。如果对字段执行算术/更新操作,@meriton 的结果表明 `byte` 可能比 `int` &gt;&gt;slow&lt;&lt;。 (6认同)