Java短整型、整型和长整型的性能表现

13

我读到JVM将short, integer和long作为4个字节进行内部存储。这是我从一篇2000年的文章中读到的,所以我不知道现在是否仍然准确。

对于较新的JVM,使用short与integer/long相比是否有性能上的提升?并且,自从2000年以来,这部分实现是否有改变?

谢谢


11
抱歉,无法用4个字节存储长整型数据。 - Michael Myers
你有这篇文章的链接吗?你确定你记得正确吗? - Peter Recore
1
shortint稍微慢一点。longint的比较取决于实际实现。通常情况下,long会更慢,但可能是微不足道的,也可能是边缘显著的。 - Hot Licks
6个回答

15

整数类型的存储方式取决于确切的类型,可能需要多个字节:

  • byte 占 8 位
  • short 占 16 位,有符号
  • int 占 32 位,有符号
  • long 占 64 位,有符号

请参见 这里的规范

至于性能方面,它取决于您对它们的使用方式。 例如,如果将文字值分配给 byte 或 short,它们将被提升为 int,因为默认情况下文字值被认为是 int 类型。

byte b = 10;  // upscaled to int, because "10" is an int

这就是为什么你不能这样做:

byte b = 10;
b = b + 1;  // Error, right member converted to int, cannot be reassigned to byte without a cast.

因此,如果您计划使用字节或短整型执行一些循环操作,那么您将不会获得任何好处。

for (byte b=0; b<10; b++) 
{ ... } 

另一方面,如果你正在使用字节或短整型数组来存储某些数据,那么你显然会从它们较小的尺寸中获益。

byte[] bytes = new byte[1000];
int[] ints = new int[1000];  // 4X the size

因此,我的答案是:这取决于情况 :)


13
long  64 –9,223,372,036,854,775,808 to 9 ,223,372,036,854,775,807 
int   32 –2,147,483,648 to 2,147,483,647 
short 16 –32,768 to 32,767 
byte   8 –128 to 127 

根据需要选择数据类型,我认为shorts很少使用是因为其范围小且它的格式为big-endian。

任何性能提升都是微不足道的,但如我所说,如果您的应用程序需要比short更大的范围,请使用int。long类型可能过于大了;但这一切都取决于您的应用程序。

只有当您关心空间(内存)时才使用short,否则在大多数情况下使用int。如果您正在创建数组等,请尝试声明int和short类型的数组。相比int,short将使用1/2的空间。 但如果您基于速度/性能运行测试,则几乎没有区别(如果您正在处理数组),此外,您节省的只是空间。

另外,因为一个评论者提到long因为长整型是64位,您将无法使用4个字节存储long的大小(请注意long的范围)。


6
长整型不是通常认为的8个字节,它被定义为64位。它始终是64位。 - Steve Kuo
6
如果每个字节有8位,这不一定是实际情况。(http://en.wikipedia.org/wiki/Byte) - Frank Meulenaar

8

这是一个实现细节,但对于性能来说仍然是真实的,大多数JVM将为每个变量使用一个完整的字(或更多),因为CPU以字单位访问内存。如果JVM将变量存储在子字单元和位置中,它实际上会更慢。

这意味着32位JVM将使用4个字节用于short(甚至是boolean),而64位JVM将使用8个字节。然而,对于数组元素,情况并非如此。


我理解你的意思是,虽然可以节省空间,但在性能方面并没有任何收益(特别是对于数组而言)。 - JonH
2
使用常规变量时,短整型在32位或64位JVM上既不节省空间也不提高性能。对于数组而言,很难说,因为将短整型转换为字并进行反向操作所需的额外CPU指令可以通过更好的缓存行为抵消由于数据大小较小而带来的优势。 - Michael Borgwardt
我同意这一点,作为常规变量,short和int的选择并不重要,关键是应用程序的需求(类型的范围)。但对于数组而言,short将比int/long更节省空间。 - JonH
即使作为成员变量,几乎所有的JVM都将short存储在16位中,而不是作为完整的字,并且64位的JVM肯定不使用64位来存储short或int。因此,不仅仅是数组,而是堆中的所有数据都受益于使用short。 - BeeOnRope

1
基本上没有区别。其中一个必须“混淆”JITC,使其无法识别增量/减量操作是自我取消的,并且结果未被使用。这样做,三种情况的结果大致相等。(实际上,short似乎稍微快一点。)
public class ShortTest {

    public static void main(String[] args){
        // Do the inner method 5 times to see how it changes as the JITC attempts to
        // do further optimizations.
        for (int i = 0; i < 5; i++) {
            calculate(i);
        }
    }

    public static void calculate(int passNum){

        System.out.println("Pass " + passNum);
        // Broke into two (nested) loop counters so the total number of iterations could
        // be large enough to be seen on the clock.  (Though this isn't as important when
        // the JITC over-optimizations are prevented.)
        int M = 100000;
        int N = 100000;
        java.util.Random r = new java.util.Random();
        short x = (short) r.nextInt(1);
        short y1 = (short) (x + 1);
        int y2 = x + 1;
        long y3 = x + 1;

        long time1=System.currentTimeMillis();
        short s=x;
        for (int j = 0; j<M;j++) {
            for(int i = 0; i<N;i++) {
                s+=y1;
                s-=1;
                if (s > 100) {
                    System.out.println("Shouldn't be here");
                }
            }
        }
        long time2=System.currentTimeMillis();
        System.out.println("Time elapsed for shorts: "+(time2-time1) + " (" + time1 + "," + time2 + ")");


        long time3=System.currentTimeMillis();
        int in=x;
        for (int j = 0; j<M;j++) {
            for(int i = 0; i<N;i++) {
                in+=y2;
                in-=1;
                if (in > 100) {
                    System.out.println("Shouldn't be here");
                }
            }
        }
        long time4=System.currentTimeMillis();
        System.out.println("Time elapsed for ints: "+(time4-time3) + " (" + time3 + "," + time4 + ")");


        long time5=System.currentTimeMillis();
        long l=x;
        for (int j = 0; j<M;j++) {
            for(int i = 0; i<N;i++) {
                l+=y3;
                l-=1;
                if (l > 100) {
                    System.out.println("Shouldn't be here");
                }
            }
        }
        long time6=System.currentTimeMillis();
        System.out.println("Time elapsed for longs: "+(time6-time5) + " (" + time5 + "," + time6 + ")");


        System.out.println(s+in+l);
    }
}

结果:

C:\JavaTools>java ShortTest
Pass 0
Time elapsed for shorts: 59119 (1422405830404,1422405889523)
Time elapsed for ints: 45810 (1422405889524,1422405935334)
Time elapsed for longs: 47840 (1422405935335,1422405983175)
0
Pass 1
Time elapsed for shorts: 58258 (1422405983176,1422406041434)
Time elapsed for ints: 45607 (1422406041435,1422406087042)
Time elapsed for longs: 46635 (1422406087043,1422406133678)
0
Pass 2
Time elapsed for shorts: 31822 (1422406133679,1422406165501)
Time elapsed for ints: 39663 (1422406165502,1422406205165)
Time elapsed for longs: 37232 (1422406205165,1422406242397)
0
Pass 3
Time elapsed for shorts: 30392 (1422406242398,1422406272790)
Time elapsed for ints: 37949 (1422406272791,1422406310740)
Time elapsed for longs: 37634 (1422406310741,1422406348375)
0
Pass 4
Time elapsed for shorts: 31303 (1422406348376,1422406379679)
Time elapsed for ints: 36583 (1422406379680,1422406416263)
Time elapsed for longs: 38730 (1422406416264,1422406454994)
0

C:\JavaTools>java -version
java version "1.7.0_65"
Java(TM) SE Runtime Environment (build 1.7.0_65-b19)
Java HotSpot(TM) 64-Bit Server VM (build 24.65-b04, mixed mode)

1
哇!所以JITC在优化短计算方面不是很好,但一旦优化完成,操作就更快了! - Federico Giorgi

0

我同意user2391480的观点,使用short类型进行计算似乎更加耗费资源。以下是一个例子,在我的机器上(Java7 64位,Intel i7-3770,Windows 7),使用short类型进行操作比使用整型和长整型慢了约50倍。

public class ShortTest {

public static void main(String[] args){
    calculate();
    calculate();
}

public static void calculate(){
    int N = 100000000;

    long time1=System.currentTimeMillis();
    short s=0;
    for(int i = 0; i<N;i++) {
        s+=1;
        s-=1;
    }
    long time2=System.currentTimeMillis();
    System.out.println("Time elapsed for shorts: "+(time2-time1));


    long time3=System.currentTimeMillis();
    int in=0;
    for(int i = 0; i<N;i++) {
        in+=1;
        in-=1;
    }
    long time4=System.currentTimeMillis();
    System.out.println("Time elapsed for ints: "+(time4-time3));


    long time5=System.currentTimeMillis();
    long l=0;
    for(int i = 0; i<N;i++) {
        l+=1;
        l-=1;
    }
    long time6=System.currentTimeMillis();
    System.out.println("Time elapsed for longs: "+(time6-time5));


    System.out.println(s+in+l);
}

}

输出:

Time elapsed for shorts: 113
Time elapsed for ints: 2
Time elapsed for longs: 2
0
Time elapsed for shorts: 119
Time elapsed for ints: 2
Time elapsed for longs: 2
0

注意:将“1”指定为短整型(以避免每次强制转换,如用户Robotnik建议的延迟源)似乎没有帮助,例如:
    short s=0;
    short one = (short)1;
    for(int i = 0; i<N;i++) {
      s+=one;
      s-=one;
    }

编辑:根据用户Hot Licks在评论中的要求进行修改,以便在主方法之外多次调用calculate()方法。


1
公平地说:将“核心代码”移动到一个单独的方法中。从main中调用它一次,丢弃结果,然后再次调用它,只保留最后一次运行的结果。这将使JITC有机会优化代码。(main中的代码几乎从不被优化。) - Hot Licks
立即尝试。是的,差异仍然存在。 - Federico Giorgi
你被JITC恶作剧了。对于int和long,它识别到+/-1操作会相互抵消,并且结果从未被使用,因此它基本上取消了整个循环。它不会花费太多精力来优化short。重写以修复这些问题,三种数据类型之间基本上没有区别。 - Hot Licks
谢谢你的投票,但请让我理解:我最后打印结果,所以它是有用的。另外,你能更具体地说明“JITC没有为短优化付出太多努力”吗?我应该如何重写操作以使其不在所有三种数据类型中取消循环? - Federico Giorgi
2
看我的答案。我从你的代码开始并进行了修改。不确定哪些功能绝对必要,但肯定取消+1/-1操作必须以某种方式“混淆”,因为即使是最愚蠢的优化器也可以弄清楚这一点。(我错过了你在最后引用结果的事实。那可能已经足够解决这个问题了。) - Hot Licks

-2

使用 short 类型进行计算非常昂贵。

以以下无用循环为例:

short t=0;
//int t=0;
//long t=0;
for(many many times...)
{
  t+=1;
  t-=1;
}

如果是short类型,它的执行时间会比int或long类型慢上数千倍。

在Linux系统上测试过64位JVM版本6/7。


2
我觉得这听起来不对,所以我试了一下。在快速测试中,短整型和整型的时间结果基本相同。你有任何数据可以支持这个吗? - Tim B
1
我刚刚在我的64位机器上进行了10亿次迭代的循环测试,每个测试运行三次。我用t += a和t -= b替换了计算,a和b都计算为1,但永远不会被编译器删除,(a =(int)Math.ceil(Math.random()),b同理)。还打印了循环的结果,以确保循环运行。使用64位JRE 6:short:1450毫秒,int:70毫秒,long:765毫秒。 - Daniel Johansson
-1 - 所声称的结果未经证实,无法通过“嗅探测试”。 - Stephen C
1
@Robotnik - 在某些架构上可能会有所作为。然而,这个答案声称有1000多倍的性能差异,这是不可信的(在我看来)。Daniel Johnson声称的结果也是如此。这种声明需要附带基准测试的完整源代码,以便其他人可以检查方法并尝试重现结果。在Java中进行微基准测试非常容易出错。 - Stephen C
1
这是我快速基准测试的代码。编译器不应该比其他情况更优化任何一种情况。http://pastebin.com/fvV1GPFH - Daniel Johansson
显示剩余6条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接