在Java中,使用字节或短而不是int和float而不是double更有效率?

我注意到,无论数量有多大,我总是使用int和double。 所以在Java中,是使用字​​节或短而不是int和float而不是double更有效?

因此,假设我有一个有很多整数和双精度的程序,如果我知道这个数字是合适的,是否值得通过并将我的整数转换成字节或短路呢?

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

通过高效我主要是指处理。 如果所有的variables都是一半大小,那么垃圾收集器的速度会快很多,而且计算速度也可能会更快一些。 (我猜,因为我在Android上工作,我也需要担心一些内存)

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

我尝试了一个小型的Android应用程序,但并没有真正注意到它们之间的差异。 (虽然我没有“科学地”衡量任何东西。)

我错在假定它应该更快,更高效? 我讨厌在一个大规模的项目中改变一切,以找出浪费我的时间。

当我开始一个新的项目时,从一开始是否值得做? (我的意思是我觉得每一点点都会有所帮助,但是如果是的话,那么为什么看起来没有人会这么做)。

非常感谢。 这只是我一直在琢磨的一个问题。

我错在假定它应该更快,更高效? 我讨厌在一个大规模的项目中改变一切,以找出浪费我的时间。

简短的回答

是的,你错了。 在大多数情况下,空间使用方面几乎没有什么区别

除非您有明确的证据certificate需要优化,否则不值得对此进行优化。 如果您确实需要优化对象字段的内存使用情况,则可能需要采取其他(更有效的)措施。

较长的答案

Java虚拟机模型堆栈和对象字段使用(实际上)32位原始单元大小的倍数的偏移量。 所以当你声明一个局部variables或对象字段为一个byte ,variables/字段将被存储在一个32位单元中,就像一个int

有两个例外:

  • longdouble值需要2个原始的32位单元
  • 原始types的数组以包装forms表示,因此(例如)一个字节数组每个32位字包含4个字节。

所以这可能是值得优化的使用longdouble …以及大量的基元。 但总的来说没有。

从理论上讲,JIT 可能能够优化这个,但实际上我从来没有听说过JIT。 一个障碍是JIT通常无法运行,直到正在编译的类的实例被创build之后。 如果JIT优化了内存布局,那么可以有两个(或更多)同一类对象的“风味”,这将带来巨大的困难。


再访

在@ meriton的回答中看到基准testing结果,似乎使用shortbyte而不是int会导致乘法的性能损失。 事实上,如果你单独考虑这个行动,那么惩罚是很重要的。 (你不应该…但这是另一回事。)

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

无论如何,这指向另一个问题切换到shortbyte作为优化。 在algorithm和计算密集的algorithm中,它可能会使性能变得更糟

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

唯一的好处是较小的基本types可以导致更紧凑的内存布局,最显着的是当使用数组时。 这样可以节省内存,从而提高引用的局部性(从而减less高速caching未命中次数)并减less垃圾收集开销。

一般来说,使用较小的原始types不会更快。

为了certificate这一点,请看下面的基准:

 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); } } } 

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

 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 

如您所见,性能差异相当小。 优化algorithm比原始types的select重要得多。

如果使用大量数据,则使用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)"); } 

}

这个类testing创build一个新的TestClass的速度。 每个testing做2000万次,有50个testing。

这里是TestClass:

  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; } 

我已经运行了SpeedTest类,最后得到了这个:

  Average Duration = 8.9625E8 ns = 896 ms (Approximately) 

现在我在TestClass中将ints更改为字节并再次运行。 结果如下:

  Average Duration = 6.94375E8 ns = 694 ms (Approximately) 

我相信这个实验表明,如果你实例化了大量的variables,使用byte而不是int可以提高效率

字节通常被认为是8位。 一般认为是16位。

在一个“纯粹的”环境中,不是所有的java都像字节和longs,shorts和其他有趣的东西一样被你隐藏起来,字节会更好地利用空间。

但是,您的计算机可能不是8位,而且可能不是16位。 这意味着特别要获得16位或8位,就需要求助于浪费时间的“欺骗”,以便假装它有能力在需要时访问这些types。

在这一点上,这取决于如何实施硬件。 然而,从我一直以来,最好的速度是从你的CPU使用舒适的块存储的东西。 一个64位的处理器喜欢处理64位的元素,而低于这个数值的任何东西通常都需要“工程魔法”才能假装它喜欢和它们打交道。

差别不明显! 这更多的是devise,适当性,统一性,习惯等问题。有时这只是一个品味的问题。 当你所关心的是你的程序启动并运行,并且用一个float代替int不会损害正确性时,除非你能certificate使用任何一个types改变性能,否则我认为没有好处。 基于2或3字节不同的types调整性能实际上是您应该关心的最后一件事情; 唐纳德·克努特(Donald Knuth)曾经说过:“不成熟的优化是万恶的根源”(不确定是否是他,如果你有答案,可以编辑)。