Skip to main content

[JSR-354] Re: Number representation on MonetaryAmount

  • From: Stephen Colebourne <scolebourne@...>
  • To: jcurrency_mail@...
  • Subject: [JSR-354] Re: Number representation on MonetaryAmount
  • Date: Sun, 29 Sep 2013 19:07:39 -0700

These results are probably about what I'd expect except that the basic
Money case using BD is faster than expected at 20 times slower.

It is a natural expectation that mixing types will result in a slower
answer, because the from() method has a greater conversion to do. Most
users of the API will be using a single money implementation - it will
be rare for them to be using more than one implementation. If they do
use more than one implementation, it is likely that the second one
will be the JDK9 implementation which a 3rd party library would be
able to refer to and optimise for.

asNumber() has never been an acceptable thing to use because the
Number interface is useless. Given a Number, there is no way to
accurately extract the numerical value. The best that can be done (and
what I assume you did) was to check instanceof BD and cast. Simply
calling doubleValue() or longValue() will never be sufficient,
especially for a money API.

In JDK9 there will be the possibility to add a factory to BD that can
create from the three arguments. Alternately, a Rational class could
be developed and proposed to the JDK separate to this JSR.

The fastest BD constructor is the (long,int) one - (unscaled, scale).

Thus the fastest BD conversion is likely to involve special casing.
Something like (but handling negatives):
if (denom == 100 && whole < Long.MAX_VALUE / 1000) {
 return BD.valueOf(whole * 100 + numerator, 2);
} else if (denom == 1) {
 return BD.valueOf(whole, 0);
} else if (denom == 1000 && whole < Long.MAX_VALUE / 10000) {
 return BD.valueOf(whole * 1000 + numerator, 3);
} else {
 ... create slow
}

or something like this
array = {1, 10, 100, 1000, ...}
for (int i = 0; i < array.length; i++) {
  if (denom == array[i]) {
    try {
      long total = Math.multiplyExact(whole, denom)
      total = Math.addExact(total, numerator);
      return BD.valueOf(total, i);
    } catch (ArithmeticException ex) {
      ... create slow
    }
  }
}
 ... create slow

Stephen


On 29 September 2013 16:58, Anatole Tresch <atsticks@...> wrote:
> Hi all/Stephen/Werner
>
> I was playing aroung with different variants of money implementations, one
> using BD, one using a single long hereby using s scale of 5 digits for
> representation of the minor units.
>
> I then measured performance for 1.000.000 executions of some arithmetic
> functions as follows:
>
> Money money1 = Money.of(EURO, BigDecimal.ONE);
> for (int i = 0; i < NUM; i++) {
> money1 = money1.add(Money.of(EURO, 1234567.3444));
> money1 = money1.subtract(Money.of(EURO, 232323));
> money1 = money1.multiply(3.4);
> money1 = money1.divide(5.456);
> }
>
> FastMoney money1 = FastMoney.of(EURO, BigDecimal.ONE);
> for (int i = 0; i < NUM; i++) {
> money1 = money1.add(FastMoney.of(EURO, 1234567.3444));
> money1 = money1.subtract(FastMoney.of(EURO, 232323));
> money1 = money1.multiply(3.4);
> money1 = money1.divide(5.456);
> }
>
> This gaves me different execution times (and results):
>
> Duration for 1000000 operations (Money/BD): 3557 ms (3 ns per loop) -> EUR
> 1657407.962529182
> Duration for 1000000 operations (FastMoney/long): 178 ms (0 ns per loop) ->
> EUR 1657407.96251
>
> When only slightly adapting the code above to:
>
> FastMoney money1 = FastMoney.of(EURO, BigDecimal.ONE);
> for (int i = 0; i < NUM; i++) {
> money1 = money1.add(Money.of(EURO, 1234567.3444));
> money1 = money1.subtract(FastMoney.of(EURO, 232323));
> money1 = money1.multiply(3.4);
> money1 = money1.divide(5.456);
> }
>
> we have quite an unexpected impact (we are ten times slower!):
>
> Duration for 1000000 operations (FastMoney/Money mixed): 1524 ms (1 ns per
> loop) -> EUR 1657407.96251
>
> The issue is, that FastMoney must convert from Money in the
> FastMoney.from(MoneytaryAmount)  using the numeric representation as
> whole,nominator/denominator . The acceptable result shown above, was
> possible, beacuse I added temporarely the method
>
> Number asNumber()
>
> to the interface and used this one, instead of creating a new Number
> instance myself. When using BigDecimal within from you are significantly
> slower (about 12 ns/loop).
>
> So question: WDYT on using asNumber() instead of the methods returning longs
> for whole, fraction nominator, fraction denominator? This would allow us to
> support a real high speed money implementation (compared to only fast one).
>
> Or can somebody provide me with code that is capable of creating a Number
> real fast from the current interface...?
>
> Or other ideas, hints?
>
> Cheers,
> Anatole
>
> PS.: Hope all is clear, its late now, and I have to end my night's hacking
> tour......
>
>
> --
> Anatole Tresch
> Java Lead Engineer, JSR Spec Lead
> Gl√§rnischweg 10
> CH - 8620 Wetzikon
>
> Switzerland, Europe Zurich, GMT+1
> Twitter:  @atsticks
> Blogs: http://javaremarkables.blogspot.ch/
> Google: atsticks
> Mobile  +41-76 344 62 79


[JSR-354] Number representation on MonetaryAmount

Anatole Tresch 09/29/2013

[JSR-354] Re: Number representation on MonetaryAmount

Stephen Colebourne 09/30/2013

[JSR-354] Re: Number representation on MonetaryAmount

Anatole Tresch 09/30/2013
 
 
Close
loading
Please Confirm
Close