【问题标题】:Most Efficient Way to Scale an Array in Java?在 Java 中扩展数组的最有效方法?
【发布时间】:2011-12-09 04:17:51
【问题描述】:

(抱歉,如果之前有人问过这个问题 - 我不敢相信它没有,但我找不到。也许我的搜索能力很弱。)

多年来,我“知道”Java 没有缩放数组的本机函数(即将每个元素乘以一个常数)。所以我一直在这样做:

for (int i=0; i<array.length; i++) {
  array[i] = array[i] * scaleFactor;
}

这实际上是最有效的方法吗(例如,在这个应用程序中,它是一个包含大约 10000 个双精度数的数组)?还是有更好的办法?

【问题讨论】:

  • 如果您对微优化感兴趣,那么您编写的内容通常可以通过使用 "loop unrolling" 来加速:en.wikipedia.org/wiki/Loop_unwinding 但是 JVM 可能已经如果代码的那部分被重复调用,那么为你这样做。 HotSpot VM 甚至有一个 XX:LoopUnrollLimit= 选项来“控制”该行为。

标签: java arrays performance vector multiplication


【解决方案1】:

在我看来绝对没问题。我想不出更有效的方法。显然尝试将代码放在一个地方,而不是将实际代码放在一个地方,但除此之外,没有明显的问题。

【讨论】:

    【解决方案2】:

    我能提供的唯一其他建议是延迟扩展,您只需支付访问每个元素的乘法成本;例如

    public class MyArray {
      private final double[] arr;
      private double scale = 1.0;
    
      public MyArray(double[] arr) {
        this.arr = arr;
      }
    
      public double getScale() {
        return scale;
      }
    
      public void setScale(double scale) {
        this.scale = scale;
      }
    
      public double elementAt(int i) {
        return arr[i] * scale;
      }
    }
    

    显然这只在某些情况下更好:

    • 当你的数组很大并且
    • 您只访问了几个元素并且
    • 您通常会访问这些元素一次。

    在其他情况下,这是一种微优化,对现代 CPU 没有真正的好处。

    【讨论】:

    • 在这种情况下,最好在内部使用 ArrayList 并在请求时将其压缩为数组。
    • @0xCAFEBABE:我想说这完全取决于 OP 是否希望数组的大小发生变化。如果没有,最好节省内存并使用原语。
    • 我的应用程序将一次读取整个数组并且它的大小是固定的,所以我的猜测是“延迟”缩放和使用 ArrayList 的效率都会降低。
    【解决方案3】:

    “更好的方法”是写array[i] *= scaleFactor; 而不是array[i] = array[i] * scaleFactor;。 :-)

    真的,这只是语法糖——编译后的输出(以及性能)应该完全相同。正如 Jon 所说,您将无法获得更好的性能,但我个人会减少任何一天的打字次数。

    【讨论】:

    • 尽管我喜欢 *= 的写作方法,但这个答案完全超出了所要求的范围,可能更适合作为评论。
    【解决方案4】:

    您可以使用线程来减少运行时间,但最重要的是您将包含此代码并让每个线程运行 for 循环的一部分,以便生成的程序与您的程序一样高效;它只是变得更快

    【讨论】:

    • 代码是(我很确定)内存限制,而不是 CPU 限制。因此,更多的线程/CPU 能力不会产生任何影响。我想它只会运行得更慢。但是,如果你写了一个程序来证明我错了,我会投赞成票!
    【解决方案5】:

    除了 Adamski 和 Jon Skeet 之外,我唯一能想到的补充一点是,如果它恰好是整数/长整数数组,并且您按 2 的幂进行缩放,那么使用位移运算符。不过 YMMV,因为它将取决于编译器(甚至可能是 VM)。

    【讨论】:

    • “这将取决于 VM”取决于编译器而不是 VM,确定吗?
    • 嗯,是的。已更正。它也可能依赖于硬件(位移可能需要乘法的时间)所以我有点支持我的观点。 :)
    • @Raedwald:它将取决于通常被认为是 VM 一部分的 JIT 编译器。优化不被认为是 Java 到字节码编译器的工作。
    【解决方案6】:

    对我来说是最佳选择。

    不要陷入错误的优化,例如在循环外的 final 字段中声明数组长度。这适用于集合,避免重复调用 .size() 方法和字符串,避免方法调用 .length() 但在数组上 .length 已经是公共最终字段。

    此外,向后循环到零可能是一种汇编语言优化,但在 Java 这样的高级语言中,VM 会处理任何明显的调整。

    【讨论】:

      【解决方案7】:

      在 Java 8 中:

      double coef = 3.0;
      double[] x1 = {1,2,3};
      double[] x2 = DoubleStream.of(x1).map(d->d*coef).toArray();
      
      System.out.println(Arrays.toString(x2));
      

      输出:[3.0, 6.0, 9.0]

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2014-04-29
        • 2013-01-21
        • 1970-01-01
        • 1970-01-01
        • 2012-05-04
        • 1970-01-01
        • 2013-04-28
        • 1970-01-01
        相关资源
        最近更新 更多