【问题标题】:Can Java optimize "mutating" BigInteger operations in loops?Java 可以在循环中优化“变异”BigInteger 操作吗?
【发布时间】:2012-05-19 18:35:55
【问题描述】:

我需要处理很多比 long (>10^200) 大得多的大数字,所以我使用 BigIntegers。我执行的最常见的操作是将它们添加到累加器中,例如:

BigInteger A = new BigInteger("0");
for(BigInteger n : nums) {
    A = A.add(n);
}

当然,为破坏性行为制作副本是一种浪费(好吧,只要有足够大的缓冲区可用),所以我想知道 Java 是否可以以某种方式优化它(我听说有一个 MutableBigInteger 类没有被数学公开.java) 或者我是否应该编写自己的 BigInteger 类。

【问题讨论】:

  • 它可以通过多种方式间接优化:例如,它可以实现大多数个新实例(除了最后一个)非常非常短地存活并且永远不会离开方法并因此优化他们的内存分配方式。这意味着很难预测这段代码的性能。您是否您的代码进行了基准测试并证明此代码是瓶颈?
  • 如果您确实需要使用可变 Integer 类,请查看:package org.apache.commons.lang.mutable
  • @anubhava:了解这些课程是件好事,但我看不出它们对这个问题有何帮助。
  • @aix:我的评论是指 OP 问题中的这一行:whether I should just write my own BigInteger class.
  • 什么是 BigNum?它不是标准的 Java 类...

标签: java


【解决方案1】:

是的,BigInteger 使用 java.math.MutableBigInteger 类进行计算密集型操作。不幸的是,它被声明为包私有,所以你不能使用它。 Apache Commons 库中还有一个“MutableBigInteger”类,但它只是 BigInteger 的可变包装器,对您没有帮助。

我想知道 Java 是否可以以某种方式对此进行优化...

不……尽管如此。

或者我是否应该编写自己的 BigInteger 类。

这是一种方法。

另一种方法是下载 OpenJDK 源代码,找到 java.math.MutableBigInteger 的源代码,更改其包名和访问权限,并将其合并到您的代码库中。唯一的障碍是 OpenJDK 是在 GPL(我认为是 GPL-2)下获得许可的,如果您曾经使用修改后的类分发代码,这会产生影响。

另见:

【讨论】:

    【解决方案2】:

    一个更快的解决方案是绕过 java 包的可见性。您可以通过在自己的项目中创建一个名为 java.math 的包并创建一个公开包 private MutableBigInteger 的公共类来做到这一点,如下所示:

    package java.math;
    
    public class PublicMutableBigInteger extends MutableBigInteger {
    
    }
    

    然后你可以只导入 java.math.PublicMutableBigInteger;并将其用作任何其他类。此解决方案速度很快,并且不会强加给您任何特定的许可。

    【讨论】:

    • 如果您要向java.math 注入一个新类(这违反了“规则”)以规避访问限制,您不妨全力以赴并修改类访问权限。真的一样的区别......
    • @Stephen C 你想说什么?我不明白你?将新类注入 java.math 肯定更快更容易。
    • 我的意思是这样的问题...stackoverflow.com/questions/860187/…
    【解决方案3】:

    编译器能做的不多,因为它不知道add 方法做了什么。这是循环体的生成代码。如您所见,它只是调用add 并存储结果。

       25:  iload   5
       27:  iload   4
       29:  if_icmpge       51
       32:  aload_3
       33:  iload   5
       35:  aaload
       36:  astore  6
       38:  aload_1
       39:  aload   6
       41:  invokevirtual   #5; //Method java/math/BigInteger.add:(Ljava/math/BigInteger;)Ljava/math/BigInteger;
       44:  astore_1
       45:  iinc    5, 1
       48:  goto    25
    

    理论上,Java 虚拟机运行时系统可能更聪明。例如,它可以检测到一个对象不断地覆盖另一个刚刚分配的对象,并为它们交换两个分配缓冲区。但是,正如我们在启用垃圾收集日志记录的情况下运行以下程序所看到的,遗憾的是情况并非如此

    import java.math.BigInteger;
    import java.util.ArrayList;
    import java.util.Random;
    
    class Test {
        public static void main(String[] args) {
        ArrayList <BigInteger> nums = new ArrayList<BigInteger>();
        final int NBITS = 100;
        final int NVALS = 1000000;
    
        System.out.println("Filling ArrayList");
        Random r = new Random();
        for (int i = 0; i < NVALS; i++)
            nums.add(new BigInteger(NBITS, r));
    
        System.out.println("Adding ArrayList values");
        BigInteger A = new BigInteger("0");
        for(BigInteger n : nums) {
            A = A.add(n);
        }
    
        System.gc();
        }
    }
    

    查看添加过程中的垃圾回收调用。

    C:\tmp>java -verbose:gc Test
    Filling ArrayList
    [GC 16256K->10471K(62336K), 0.0257655 secs]
    [GC 26727K->21107K(78592K), 0.0304749 secs]
    [GC 53619K->42090K(78592K), 0.0567912 secs]
    [Full GC 42090K->42090K(122304K), 0.1019642 secs]
    [GC 74602K->65857K(141760K), 0.0601406 secs]
    [Full GC 65857K->65853K(182144K), 0.1485418 secs]
    Adding ArrayList values
    [GC 117821K->77213K(195200K), 0.0381312 secs]
    [GC 112746K->77245K(228288K), 0.0111372 secs]
    [Full GC 77245K->137K(228288K), 0.0327287 secs]
    
    C:\tmp>java -version
    java version "1.6.0_25"
    Java(TM) SE Runtime Environment (build 1.6.0_25-b06)
    Java HotSpot(TM) 64-Bit Server VM (build 20.0-b11, mixed mode)
    

    【讨论】:

    • 您能否描述一下如何生成汇编列表?
    • 你只需运行javap -c Test
    • 当然,我一定是在睡觉。那是java字节码,而不是热点生成的汇编程序。因此,它并没有说明 jit 发挥作用后的实际性能。
    【解决方案4】:

    Java 不会针对这种情况进行任何特殊优化。 BigInteger 通常被视为与任何其他类一样的普通类(例如,与 String 不同,当您连接许多字符串时,它有时会得到一些特殊的优化)。

    但在大多数情况下,BigInteger 足够快,无论如何它都无关紧要。如果您真的认为这可能是个问题,我建议您分析您的代码并找出花费时间的原因。

    如果添加 BigIntegers 确实是您的瓶颈,那么可能使用自定义的可变大整数类作为累加器是有意义的。但在你证明这确实是主要瓶颈之前,我不会这样做。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2017-02-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-10-25
      • 1970-01-01
      • 2017-11-05
      • 1970-01-01
      相关资源
      最近更新 更多