What to do with Java BigDecimal performance?

Viewed 42924

I write currency trading applications for living, so I have to work with monetary values (it's a shame that Java still doesn't have decimal float type and has nothing to support arbitrary-precision monetary calculations). "Use BigDecimal!" — you might say. I do. But now I have some code where performance is an issue, and BigDecimal is more than 1000 times (!) slower than double primitives.

The calculations are very simple: what the system does is calculating a = (1/b) * c many many times (where a, b and c are fixed-point values). The problem, however, lies with this (1/b). I can't use fixed point arithmetic because there is no fixed point. And BigDecimal result = a.multiply(BigDecimal.ONE.divide(b).multiply(c) is not only ugly, but sluggishly slow.

What can I use to replace BigDecimal? I need at least 10x performance increase. I found otherwise excellent JScience library which has arbitrary-precision arithmetics, but it's even slower than BigDecimal.

Any suggestions?

19 Answers

May be you should start with replacing a = (1/b) * c with a = c/b ? It's not 10x, but still something.

If I were you, I'd create my own class Money, which would keep long dollars and long cents, and do math in it.

Most double operations give you more than enough precision. You can represent $10 trillion with cent accuracy with double which may be more than enough for you.

In all the trading systems I have worked on (four different banks), they have used double with appropriate rounding. I don't see any reason to be using BigDecimal.

Assuming you can work to some arbitrary but known precision (say a billionth of a cent) and have a known maximum value you need handle (a trillion trillion dollars?) you can write a class which stores that value as an integer number of billionths of a cent. You'll need two longs to represent it. That should be maybe ten times as slow as using double; about a hundred times as fast as BigDecimal.

Most of the operations are just performing the operation on each part and renormalizing. Division is slightly more complicated, but not much.

EDIT:In response to the comment. You will need to implement a bitshift operation on your class (easy as along as the multiplier for the high long is a power of two). To do division shift the divisor until it's not quite bigger than the dividend; subtract shifted divisor from dividend and increment the result (with appropriate shift). Repeat.

EDIT AGAIN:You may find BigInteger does what you need here.

Store longs as the number of cents. For example, BigDecimal money = new BigDecimal ("4.20") becomes long money = 420. You just have to remember to mod by 100 to get dollars and cents for output. If you need to track, say, tenths of a cent, it'd become long money = 4200 instead.

You might want to move to fixed point math. Just searching for some libraries right now. on sourceforge fixed-point I haven't looked at this in depth yet. beartonics

Did you test with org.jscience.economics.money? since that has assured accuracy. The fixed point will only be as accurate as the # of bits assigned to each piece, but is fast.

Personally, I don't think BigDecimal is ideal for this.

You really want to implement your own Money class using longs internally to represent the smallest unit (i.e. cent, 10th cent). There is some work in that, implementing add() and divide() etc, but it's not really that hard.

What version of the JDK/JRE are you using?

Also you might try ArciMath BigDecimal to see if theirs speeds it up for you.

Edit:

I remember reading somewhere (I think it was Effective Java) that the BigDecmal class was changed from being JNI called to a C library to all Java at some point... and it got faster from that. So it could be that any arbitrary precision library you use is not going to get you the speed you need.

Only 10x performance increase desired for something that is 1000x slower than primitive?!.

Throwing a bit more hardware at this might be cheaper (considering the probability of having a currency calculation error).

1/b is not exactly representable with BigDecimal either. See the API docs to work out how the result is rounded.

It shouldn't be too difficult to write your own fixed decimal class based around a long field or two. I don't know any appropriate off the shelf libraries.

Is JNI a possibility? You may be able to recover some speed and potentially leverage existing native fixed point libraries (maybe even some SSE* goodness too)

Perhaps http://gmplib.org/

Had a similar problem to this in an equity trading system back in 99. At the very start of the design we choose to have every number in the system represented as a long multiplied by 1000000 thus 1.3423 was 1342300L. But the main driver for this was memory foot print rather than straight line performance.

One word on caution, I wouldn't do this again today unless I was really sure that the math performance was super critical. In most bog standard webapps the overhead of jdbc access and accessing other network resources swamps any benefit of having really quick math.

I know this is a really old thread, but i am writing an app (incidentally a trading app), in which computation of the indicators like MACD (which computes multiple exponential moving averages) over several thousand ticks of historical candlesticks was taking an unacceptable amount of time (several minutes). I was using BigDecimal.

every time i scrolled or resized the window, it would have to just iterate through the cached values to resize the Y scale, but even that would take several seconds to update. it made the app unusable. every time i would tweak the parameters for various indicators, it would take several minutes again to recompute.

then i switched it all to double and it's sooooo much faster. the problem was that i cache values using a hashmap. the solution i came up with uses a pool of wrappers for the double values. by pooling the wrappers, you don't take the performance hit of autoboxing to/from Double.

the app now calculates MACD (+MACD signal, MACD histogram) instantly with no lag. it's amazing how expensive BigDecimal object creation was. think about something like a.add( b.multiply( c )).scale(3) and how many objects that one statement creates.

 import java.util.HashMap;

public class FastDoubleMap<K>
{
    private static final Pool<Wrapper> POOL = new Pool<FastDoubleMap.Wrapper>()
    {
        protected Wrapper newInstance()
        {
            return new Wrapper();
        }
    };
    
    private final HashMap<K, Wrapper> mMap;
    
    public FastDoubleMap()
    {
        mMap = new HashMap<>();
    }

    public void put( K pKey, double pValue )
    {
        Wrapper lWrapper = POOL.checkOut();
        lWrapper.mValue = pValue;
        mMap.put( pKey, lWrapper );
    }
    
    public double get( K pKey )
    {
        Wrapper lWrapper  = mMap.get( pKey );
        if( lWrapper == null )
        {
            return Double.NaN;
        }
        else
        {
            return lWrapper.mValue;
        }
    }
    
    public double remove( K pKey )
    {
        Wrapper lWrapper = mMap.remove( pKey );
        if( lWrapper != null )
        {
            double lDouble = lWrapper.mDouble;
            POOL.checkIn( lWrapper );
            return lDouble;
        }
        else
        {
            return Double.NaN;
        }
    }

    private static class Wrapper
        implements Pooled
    {
        private double mValue ;
        
        public void cleanup()
        {
            mValue = Double.NaN;
        }
    }
}
Related