Wil*_*zel 134 java optimization types java-api
为什么Java API会使用int,何时short甚至byte是足够的?
示例:DAY_OF_WEEK类中的字段Calendar使用int.
如果差异太小,那么为什么存在这些数据类型(short,int)?
Mar*_*o13 164
已经指出了一些原因.例如,"......(几乎)所有对byte的操作,short将把这些原语提升为int".然而,明显的下一个问题是:为什么这些类型被提升为int?
所以更深入一层:答案可能只与Java虚拟机指令集有关.如Java虚拟机规范中的表中所总结的,所有整数算术运算(如添加,除法等)仅适用于类型int和类型long,而不适用于较小类型.
(旁白:较小的类型(byte和short)基本上只用于数组.类似的数组new byte[1000]将占用1000个字节,类似的数组new int[1000]将占用4000个字节)
当然,现在,人们可以说"......明显的下一个问题是:为什么这些指示仅适用于int(和long)?" .
上面提到的JVM规范中提到了一个原因:
如果每个类型化指令都支持所有Java虚拟机的运行时数据类型,则会有更多的指令可以在一个字节中表示
此外,Java虚拟机可以被视为真实处理器的抽象.为较小的类型引入专用的算术逻辑单元是不值得的:它需要额外的晶体管,但它仍然只能在一个时钟周期内执行一次加法.JVM设计时的主流架构是32位,恰好适用于32位int.(涉及64位long值的操作是作为特殊情况实现的).
(注意:最后一段有点过于简单,考虑到可能的矢量化等,但应该给出基本的想法,而不要过多深入处理器设计主题)
编辑:一个简短的附录,侧重于问题的例子,但在更广泛的意义上:人们还可以问,使用较小的类型存储字段是否有益.例如,人们可能会认为记忆可以通过存储保存Calendar.DAY_OF_WEEK的byte.但是在这里,Java类文件格式发挥作用:类文件中的所有字段占用至少一个"槽",其大小为一int(32位).(其中"宽"域,double并且long,占用两个时隙).所以明确地声明一个字段short或者byte不保存任何内存.
Mar*_*oun 39
(差不多)所有的操作byte,short都会促使他们int,例如,你不能写:
short x = 1;
short y = 2;
short z = x + y; //error
Run Code Online (Sandbox Code Playgroud)
算术在使用时更容易,更直接int,无需施法.
在空间方面,它使一个非常小的差异.byte并且short会使事情变得复杂,我不认为这个微优化是值得的,因为我们讨论的是固定数量的变量.
byte在为嵌入式设备编程或处理文件/网络时,它是相关且有用的.这些原语也是有限的,如果计算可能会超出他们未来的限制怎么办?尝试考虑Calendar可能演变更大数字的类的扩展.
还要注意的是,在64位处理器,当地人将被保存在寄存器中,不会使用任何资源,因此,使用int,short和其他原语不会做出任何差别.此外,许多Java实现对齐变量*(和对象).
* byte并short占用相同的空间,就int好像它们是局部变量,类变量甚至实例变量一样.为什么?因为在(大多数)计算机系统中,变量地址是对齐的,所以例如如果使用单个字节,实际上最终会得到两个字节 - 一个用于变量本身,另一个用于填充.
另一方面,在数组中,byte取1个字节,short占用2个字节并int占用4个字节,因为在数组中只需要对齐它的开始和结束.例如,如果你想使用它会产生影响System.arraycopy(),那么你会真正注意到性能差异.
因为与短裤相比,使用整数时算术运算更容易.假设常量确实是由short值建模的.然后你必须以这种方式使用API:
short month = Calendar.JUNE;
month = month + (short) 1; // is july
Run Code Online (Sandbox Code Playgroud)
注意显式铸造.int在算术运算中使用短值时,会将其隐式提升为值.(在操作数堆栈上,short甚至表示为int.)这使用起来非常麻烦,这就是为什么int值通常是常量的首选.
与此相比,存储效率的提高是最小的,因为只存在固定数量的这种常数.我们谈论的是40个常数.将存储空间从更改int为short安全40 * 16 bit = 80 byte.请参阅此答案以获取进一步参考
小智 5
如果你使用了积分常量存储在它们所适合的最小类型中的哲学,那么Java会有一个严重的问题:每当程序员使用积分常量编写代码时,他们必须仔细注意他们的代码以检查是否有类型常量很重要,如果是这样,请查看文档中的类型和/或进行所需的任何类型转换.
那么既然我们已经概述了一个严重的问题,那么您希望通过这种理念获得哪些好处?如果通过反射看到常量变化,那么变化的唯一运行时可观察效果就是你得到的类型,我不会感到惊讶.(当然,懒惰/不知情程序员引入的错误不能正确地解释常量的类型)
权衡利弊是非常容易的:这是一个糟糕的哲学.
虚拟机的设计复杂性是它可以执行多少种操作的函数。有四个像“乘法”这样的指令的实现——一个分别用于 32 位整数、64 位整数、32 位浮点数和 64 位浮点数——比另外实现更容易对于上述,较小的数字类型的版本也是如此。一个更有趣的设计问题是为什么应该有四种类型,而不是更少(使用 64 位整数执行所有整数计算和/或使用 64 位浮点值执行所有浮点计算)。使用 32 位整数的原因是,Java 预计可以在许多平台上运行,在这些平台上,32 位类型可以像 16 位或 8 位类型一样快速,但对 64 位类型的操作会很明显慢点。只有32 位类型。
至于对 32 位值执行浮点计算,优势有点不太清楚。有一些平台,其中的计算像float a=b+c+d;通过将所有操作数转换为更高精度的类型,将它们相加,然后将结果转换回 32 位浮点数以进行存储,可以最快地执行此操作。在其他平台上,使用 32 位浮点值执行所有计算会更有效。Java 的创造者决定应该要求所有平台以相同的方式做事,并且他们应该支持 32 位浮点计算比长计算更快的硬件平台,尽管这严重降低了 PC 的速度以及在典型 PC 上以及许多没有浮点单元的机器上的浮点数学精度。注意,顺便说一句,根据 b、c 和 d 的值,在计算上述表达式时使用更高精度的中间计算float a=b+c+d;有时会产生比以float精度计算的所有中间操作数更准确的结果,但有时会产生一个不太准确的值。无论如何,Sun 决定一切都应该以相同的方式完成,并且他们选择使用最小精度float值。
请注意,当大量数据类型一起存储在一个数组中时,较小数据类型的主要优势变得明显。即使拥有小于 64 位类型的单个变量没有任何优势,拥有可以更紧凑地存储较小值的数组也是值得的;将局部变量设为 abyte而不是 an 可long节省七个字节;拥有 1,000,000 个数字的数组将每个数字作为一个byte而不是一个long波 7,000,000 字节。由于每种数组类型只需要支持几个操作(最显着的是读取一个项目、存储一个项目、复制数组中的一系列项目或将一系列项目从一个数组复制到另一个),因此增加了更多的复杂性数组类型不像拥有更多类型的可直接使用的离散数值那么复杂。
| 归档时间: |
|
| 查看次数: |
11666 次 |
| 最近记录: |