16

I am doing calculations with BigIntegers that uses a loop that calls multiply() about 100 billion times, and the new object creation from the BigInteger is making it very slow. I was hoping somebody had written or found a MutableBigInteger class. I found the MutableBigInteger in the java.math package, but it is private and when I copy the code into a new class, many errors come up, most of which I don't know how to fix.

What implementations exist of a Java class like MutableBigInteger that allows modifying the value in place?

Peter O.
  • 32,158
  • 14
  • 82
  • 96
Fractaly
  • 834
  • 2
  • 10
  • 25
  • 8
    How did you determine the creation of a BigInteger is what makes it slow? – millimoose Oct 22 '11 at 22:30
  • 1
    It is fast with primitives, and I know object creation has significant, overhead, so I think it is creation causing most of the delay. – Fractaly Oct 22 '11 at 22:39
  • 1
    Some part of the answer may be found here: http://stackoverflow.com/questions/890968/what-is-the-purpose-of-java-math-mutablebiginteger – Phil Oct 22 '11 at 22:42
  • my solution for creating a class in java.math unfortunately did not work. throws java.lang.SecurityException: Prohibited package name: java.math – MarianP Oct 23 '11 at 00:23
  • Thanks for trying. I'm writing my own mutable arbitrary size integer class. I'll share when I'm done – Fractaly Oct 23 '11 at 02:39
  • 1
    Add the class to your `public` version to your boot class path or `jre/lib/endorsed` directory to avoid this error/warning. – Peter Lawrey Oct 23 '11 at 06:46
  • 1
    My jre/lib has no endorsed directory. Making a BigInteger class is also much more difficult than I thought. I am still struggling with BigInteger performance for cryptography. Before it was for fractals. – Fractaly Dec 06 '11 at 00:45
  • 100 billion times? – phuclv Mar 22 '16 at 02:49

3 Answers3

11

Is their any particular reason you cannot use reflection to gain access to the class?

I was able to do so without any problems, here is the code:

public static void main(String[] args) throws Exception {       
    Constructor<?> constructor = Class.forName("java.math.MutableBigInteger").getDeclaredConstructor(int.class);
    constructor.setAccessible(true);
    Object x = constructor.newInstance(new Integer(17));
    Object y = constructor.newInstance(new Integer(19));
    Constructor<?> constructor2 = Class.forName("java.math.MutableBigInteger").getDeclaredConstructor(x.getClass());
    constructor2.setAccessible(true);
    Object z = constructor.newInstance(new Integer(0));
    Object w = constructor.newInstance(new Integer(0));

    Method m = x.getClass().getDeclaredMethod("multiply", new Class[] { x.getClass(), x.getClass()});
    Method m2 = x.getClass().getDeclaredMethod("mul", new Class[] { int.class, x.getClass()});
    m.setAccessible(true);
    m2.setAccessible(true);

    // Slightly faster than BigInteger
    for (int i = 0; i < 200000; i++) {
        m.invoke(x, y, z);
        w = z;
        z = x;
        x = w;
    }

    // Significantly faster than BigInteger and the above loop
    for (int i = 0; i < 200000; i++) {
        m2.invoke(x, 19, x);
    }

    BigInteger n17 = new BigInteger("17");
    BigInteger n19 = new BigInteger("19");
    BigInteger bigX = n17;

    // Slowest
    for (int i = 0; i < 200000; i++) {
        bigX = bigX.multiply(n19);
    }
}

Edit: I decided to play around with a bit more, it does appear that java.math.MutableBigInteger doesn't behave exactly as you would expect.

It operates differently when you multiply and it will throw a nice exception when it has to increase the size of the internal array when assigning to itself. Something I guess is fairly expected. Instead I have to swap around the objects so that it is always placing the result into a different MutableBigInteger. After a couple thousand calculations the overhead from reflection becomes negligible. MutableBigInteger does end up pulling ahead and offers increasingly better performance as the number of operations increases. If you use the 'mul' function with an integer primitive as the value to multiply with, the MutableBigInteger runs almost 10 times faster than using BigInteger. I guess it really boils down to what value you need to multiply with. Either way if you ran this calculation "100 billion times" using reflection with MutableBigInteger, it would run faster than BigInteger because there would be "less" memory allocation and it would cache the reflective operations, removing overhead from reflection.

Jyro117
  • 4,519
  • 24
  • 29
  • 1
    The reason for wanting to use MutableBigInteger is performance. Using reflection would almost certainly result in much worse performance than using BigInteger. – Michael Borgwardt Dec 20 '11 at 23:29
  • I can't see how performance could be much worse if the operation is being repeatedly performed? – Jyro117 Dec 21 '11 at 06:40
  • 2
    even when the Method objec is reused, invoke() is still slower than a direct method invocation, but apparently this has now been optimized to the point where it does not dominate other factors. Props for doing benchmarks - but note that they will depend very much on the JVM version. – Michael Borgwardt Dec 21 '11 at 08:47
  • 2
    I absolutely agree with the JVM version/type used as a major factor in performance. If this was for an Android application I would definitely not be recommending this :) – Jyro117 Dec 21 '11 at 17:55
  • Wow! that is very cool. I will look at it again in the morning. (It's 1:00 AM) – Fractaly Dec 23 '11 at 08:56
3

JScience has a class call LargeInteger, which is also immutable, but which they claim has significantly improved perfomance compared to BigInteger.

http://jscience.org/

APFloat's Apint might be worth checking out too. http://www.apfloat.org/apfloat_java/

MarianP
  • 2,719
  • 18
  • 17
  • I used Apfloat, and it was worse than bigdecimal. I'm not hopeful that apint will be better than BigInteger. – Fractaly Oct 23 '11 at 02:38
  • using the jre/lib/endorsed as in the comment by Peter Lawrey might be the way to use MutableBigInteger – MarianP Oct 23 '11 at 10:37
1

I copied MutableBigInteger, then commented out some methods' bodies I dind't need, adding a nice

throw new UnsupportedOperationException("...");

when invoked.

here is how it looks.

In Revisions you can see what's changed from the original java.math.MutableBigInteger.

I also added some convenience methods,

public void init(long val) {};
public MutableBigInteger(long val) {};
// To save previous value before modifying.
public void addAndBackup(MutableBigInteger addend) {}
// To restore previous value after modifying.  
public void restoreBackup() {}

Here is how I used it:

private BigInteger traverseToFactor(BigInteger offset, BigInteger toFactorize, boolean forward) {
    MutableBigInteger mbiOffset = new  MutableBigInteger(offset);
    MutableBigInteger mbiToFactorize = new MutableBigInteger(toFactorize);
    MutableBigInteger blockSize = new MutableBigInteger(list.size);

    if (! MutableBigInteger.ZERO.equals(mbiOffset.remainder(blockSize))) {
        throw new ArithmeticException("Offset not multiple of blockSize");
    }

    LongBigArrayBigList pattern = (LongBigArrayBigList) list.getPattern();

    while (true) {
        MutableBigInteger divisor = new MutableBigInteger(mbiOffset);
        for (long i = 0; i < pattern.size64(); i++) {
            long testOperand = pattern.getLong(i);
            MutableBigInteger.UNSAFE_AUX_VALUE.init(testOperand);
            divisor.addAndBackup(MutableBigInteger.UNSAFE_AUX_VALUE);
            if (MutableBigInteger.ZERO.equals(mbiToFactorize.remainder(divisor))) {
                return divisor.toBigInteger();
            }
            divisor.restoreBackup();
        }

        if (forward) {
            mbiOffset.add(blockSize);
        } else {
            mbiOffset.subtract(blockSize);
        }
        System.out.println(mbiOffset);
    }
}
juanmf
  • 2,002
  • 2
  • 26
  • 28
  • After profiling a bit, I notices that `add(X) & substract(X)` is much faster than `addAndBackup(X) & restoreBackup()` about 1000 times faster. – juanmf Mar 22 '16 at 15:51