通过向下转换,性能/内存的好处是否无效?

时间:2012-11-27 16:56:28

标签: java performance primitive downcast

我正在编写一个大型应用程序,我试图尽可能多地保存内存并提高性能。因此,当我知道某个字段的值只有0 - 10或-100 - 100时,我会尝试使用short数据类型而不是int

这对于其余代码来说意味着,当我调用这些函数时,我必须将简单的int向下转换为short s。例如:

方法签名

public void coordinates(short x, short y) ...

方法调用

obj.coordinates((short) 1, (short) 2);

就像我的代码中的所有代码一样,因为文字被视为int,并且不会根据函数参数自动向下或输入。

因此,一旦这种向下转发发生,任何性能或内存增益是否真的显着?或者转换过程是如此有效,以至于我仍然可以获得一些收益?

5 个答案:

答案 0 :(得分:10)

在32位平台上使用short与int没有性能优势,除了short []与int []之外的所有情况 - 即使这样,缺点通常也超过了专业人士。

假设你在x64,x86或ARM-32上运行:

  • 使用时,16位SHORT存储在整数寄存器中,这些寄存器为32位或64位长,与整数相同。即当使用短线时,与int相比,你没有获得任何记忆或性能优势。
  • 当在堆栈上时,16位SHORT存储在32位或64位“槽”中,以保持堆栈对齐(就像整数一样)。对于局部变量,使用SHORTs与INTs不会获得性能或内存优势。
  • 当作为参数传递时,SHORT在被推入堆栈时自动加宽为32位或64位(与刚刚推入的整数不同)。这里的代码实际上性能稍差,并且比使用ints时(或代码)内存占用量略大。
  • 存储全局(静态)变量时,这些变量会自动扩展为占用32位或64位插槽,以保证指针(引用)的对齐。这意味着对于全局(静态)变量使用SHORTs与INTs没有任何性能或内存优势。
  • 存储字段时,它们存在于堆内存中映射到类布局的结构中。在此类中,字段会自动填充为32位或64位,以维护堆上字段的对齐方式。通过对字段和INT使用SHORT,您不会获得性能或内存优势。

在使用SHORTs与INTs时,您将看到的唯一好处是在您分配数组的情况下。在这种情况下, N 短路的数组大约是 N 整数数组的一半。

除了在大量短路中复杂但本地化数学的情况下将变量放在热循环中所带来的性能优势之外,您将永远看不到使用SHORTS与INT的好处。

ALL 其他情况下 - 例如用于字段,全局变量,参数和本地的短路,除了它可以存储的位数之外,还有 SHORT和INT之间的区别。

我的建议一如既往地建议在使代码更难以阅读和更人为限制之前,尝试使用 BENCHMARKING 代码来查看内存和CPU瓶颈的位置,然后解决这些问题。

我强烈怀疑,如果你曾经遇到过你的应用程序遭受使用而不是短路的情况,那么你很快就会抛弃Java以获得更少的内存/ CPU饥饿运行时间,所以这项工作是预先付出的努力。

答案 1 :(得分:7)

据我所知,强制转换本身应该没有运行时成本(使用short而不是int实际上提高性能是有争议的,并且取决于应用程序的细节)。

请考虑以下事项:

public class Main {
    public static void f(short x, short y) {
    }

    public static void main(String args[]) {
        final short x = 1;
        final short y = 2;
        f(x, y);
        f((short)1, (short)2);
    }
}

main()的最后两行编译为:

  // f(x, y)
   4: iconst_1      
   5: iconst_2      
   6: invokestatic  #21                 // Method f:(SS)V

  // f((short)1, (short)2);
   9: iconst_1      
  10: iconst_2      
  11: invokestatic  #21                 // Method f:(SS)V

如您所见,它们完全相同。演员阵容发生在编译时。

答案 2 :(得分:2)

int文字到short的类型转换在编译时发生,并且没有运行时性能影响。

答案 3 :(得分:1)

您需要一种方法来检查类型选择对内存使用的影响。如果在给定情况下短路与整流将通过较低的内存占用来获得性能,那么对内存的影响应该是可测量的。

这是一种测量使用内存量的简单方法:

      private static long inUseMemory() {
        Runtime rt = Runtime.getRuntime();
        rt.gc();
        final long memory = rt.totalMemory() - rt.freeMemory();
        return memory;
      }

我还提供了一个程序示例,该程序使用该方法在某些常见情况下检查内存使用情况。分配一百万个短路阵列的内存增加证实了短阵列每个元素使用两个字节。各种对象数组的内存增加表明改变一个或两个字段的类型几乎没有区别。

这是一次运行的输出。 YMMV。

Before short[1000000] allocation: In use: 162608 Change 162608
After short[1000000] allocation: In use: 2162808 Change 2000200
After TwoShorts[1000000] allocation: In use: 34266200 Change 32103392
After NoShorts[1000000] allocation: In use: 58162560 Change 23896360
After TwoInts[1000000] allocation: In use: 90265920 Change 32103360
Dummy to keep arrays live -378899459

本文的其余部分是程序源:

    public class Test {
      private static int BIG = 1000000;
      private static long oldMemory = 0;

      public static void main(String[] args) {
        short[] megaShort;
        NoShorts[] megaNoShorts;
        TwoShorts[] megaTwoShorts;
        TwoInts[] megaTwoInts;
        System.out.println("Before short[" + BIG + "] allocation: "
            + memoryReport());
        megaShort = new short[BIG];
        System.out
            .println("After short[" + BIG + "] allocation: " + memoryReport());
        megaTwoShorts = new TwoShorts[BIG];
        for (int i = 0; i < BIG; i++) {
          megaTwoShorts[i] = new TwoShorts();
        }
        System.out.println("After TwoShorts[" + BIG + "] allocation: "
            + memoryReport());
        megaNoShorts = new NoShorts[BIG];
        for (int i = 0; i < BIG; i++) {
          megaNoShorts[i] = new NoShorts();
        }
        System.out.println("After NoShorts[" + BIG + "] allocation: "
            + memoryReport());
        megaTwoInts = new TwoInts[BIG];
        for (int i = 0; i < BIG; i++) {
          megaTwoInts[i] = new TwoInts();
        }
        System.out.println("After TwoInts[" + BIG + "] allocation: "
            + memoryReport());

        System.out.println("Dummy to keep arrays live "
            + (megaShort[0] + megaTwoShorts[0].hashCode() + megaNoShorts[0]
                .hashCode() + megaTwoInts[0].hashCode()));

      }

      private static long inUseMemory() {
        Runtime rt = Runtime.getRuntime();
        rt.gc();
        final long memory = rt.totalMemory() - rt.freeMemory();
        return memory;
      }

      private static String memoryReport() {
        long newMemory = inUseMemory();
        String result = "In use: " + newMemory + " Change "
            + (newMemory - oldMemory);
        oldMemory = newMemory;
        return result;
      }
    }

    class NoShorts {
      //char a, b, c;
    }

    class TwoShorts {
      //char a, b, c;
      short s, t;
    }

    class TwoInts {
      //char a, b, c;
      int s, t;
    }

答案 4 :(得分:-1)

首先,我想确认节省的内存,因为我看到了一些疑问。根据此处教程中short的文档:http://docs.oracle.com/javase/tutorial/java/nutsandbolts/datatypes.html

  

:短数据类型是16位带符号的二进制补码整数。它的最小值为-32,768,最大值为32,767(含)。与字节一样,适用相同的准则:在内存节省实际上很重要的情况下,您可以使用短路来节省大型阵列中的内存。

使用short您确实将内存保存在大型数组中(希望是这种情况),因此最好使用它。

现在回答你的问题:

  

预测的性能/内存优势是否会因向下转换而无效?

简短回答是否。intshort向下投射在编译时发生,因此从性能角度来看没有下降影响,但因为您正在保存内存,它可能会在内存阈值场景中带来更好的性能