6

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

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

方法签名

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

方法调用

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

在我的代码中都是这样,因为文字被视为ints 并且不会根据函数参数自动向下转换或键入。

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

4

5 回答 5

10

在 32 位平台上使用 short 与 int 并没有性能优势,除了 short[] 与 int[] 的情况外 - 即便如此,缺点通常超过优点。

假设您在 x64、x86 或 ARM-32 上运行:

  • 使用时,16 位 SHORT 存储在 32 位或 64 位长的整数寄存器中,与整数相同。即,当使用short 时,与int 相比,您不会获得内存或性能优势。
  • 在堆栈上时,16 位 SHORT 存储在 32 位或 64 位“槽”中,以保持堆栈对齐(就像整数一样)。将 SHORT 与 INT 用于局部变量没有任何性能或内存优势。
  • 当作为参数传递时,SHORT 在被压入堆栈时会自动扩展为 32 位或 64 位(与刚刚压入的 int 不同)。与使用 int 相比,此处的代码实际上性能略低,(代码)内存占用略大。
  • 在存储全局(静态)变量时,这些变量会自动扩展为占用 32 位或 64 位插槽,以保证指针(引用)对齐。这意味着将 SHORT 与 INT 用于全局(静态)变量相比,您不会获得性能或内存优势。
  • 存储字段时,它们存在于堆内存中的结构中,该结构映射到类的布局。在此类中,字段会自动填充为 32 位或 64 位,以保持堆上字段的对齐。将 SHORT 用于字段与 INT 相比,您不会获得性能或内存优势。

使用 SHORT 与 INT 相比,您将看到的唯一好处是在您分配它们的数组的情况下。在这种情况下,N个short 数组的长度大约是N个int 数组的一半。

除了在大量短裤中进行复杂但局部数学运算的情况下,在热循环中将变量放在一起带来的性能优势之外,您永远不会看到使用 SHORTS 与 INT 相比的优势。

所有其他情况下——例如用于字段、全局变量、参数和局部变量的 short,除了它可以存储的位数之外, SHORT 和 INT 之间没有区别。

我的建议一如既往地建议,在使您的代码更难阅读和人为限制之前,请尝试对代码进行基准测试以查看内存和 CPU 瓶颈在哪里,然后解决这些问题。

我强烈怀疑,如果您遇到过您的应用程序使用整数而不是短裤的情况,那么您早就放弃 Java 以获得更少的内存/CPU 运行时,所以提前完成所有这些工作是白费力气。

于 2012-11-27T17:25:01.487 回答
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

如您所见,它们是相同的。强制转换发生在编译时。

于 2012-11-27T17:09:43.180 回答
2

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

于 2012-11-27T17:09:23.293 回答
1

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

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

      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;
    }
于 2012-11-27T17:54:20.350 回答
-1

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

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

通过使用short您确实可以将内存保存在大型数组中(希望是这种情况),因此使用它是个好主意。

现在回答你的问题:

向下转换是否会抵消 short 的性能/内存优势?

简短的回答是否定的。向下转换 from inttoshort发生在编译时本身,因此从性能角度来看没有向下影响,但由于您正在节省内存,它可能会在内存阈值场景中产生更好的性能

于 2012-11-27T17:05:38.893 回答