On Fri, Jun 4, 2010 at 3:04 AM, John Cowan <[email protected]> wrote:
> I actually tested Integers (with autoboxing) against BigIntegers on a
> 32-bit system (I don't have a 64-bit system at present), and I made
> sure that the arithmetic operations I was performing never overflowed
> 32 bits. The additional cost of using BigIntegers was between 2x and
> 3x, which I assume is the joint cost of:
>
> 1) overflow tests that fail;
>
> 2) the additional indirection: an Integer contains an int, whereas a
> BigInteger contains an array with one int.
>
> That seems to me small compared to having to handle two integer
> representations, with special provisions for mixed mode and so on.
Well dealing with the two isn't that big a deal; if the overflow check
fails, we just construct the BigInteger version and do the math again.
That's essentially the same logic you'd have to do with any
split-brain arbitrary-precision integer implementation. Of course
you're talking about whether just using BigInteger to begin with would
be faster.
Looking at size, if we're talking about Long it's object + long field.
We can ignore the object since that's the same for BigInteger, and
then the additional size is just 8 bytes. For BigInteger, it's object
+ int + int[] + (the remaining all deprecated, but *still there) four
more ints = 24-28 bytes, and can only represent up to 32-bit values
before adding the array, which is something like 8-12 bytes for the
header and then 4 bytes for each entry. So in terms of allocation
alone, BigInteger is several times larger than Long.
The overflow check is nontrivial. Here's the opto assembly for it (no
inlining, full method body, so some of this would disappear when
inlined):
000 B1: # B4 B2 <- BLOCK HEAD IS JUNK Freq: 1
000 PUSHL EBP
SUB ESP,8 # Create frame
007 MOV ECX,[ESP + #24]
MOV EBX,[ESP + #28]
00f XOR ECX.lo,[ESP + #16]
XOR ECX.hi,[ESP + #16]+4
017 MOV EBP,[ESP + #32]
MOV EDI,[ESP + #36]
01f XOR EBP.lo,[ESP + #16]
XOR EBP.hi,[ESP + #16]+4
027 AND ECX.lo,EBP.lo
AND ECX.hi,EBP.hi
02b AND ECX.lo,#-9223372036854775808.lo
AND ECX.hi,#-9223372036854775808.hi
034 MOV EDI,ECX.lo
OR EDI,ECX.hi ! Long is EQ/NE 0?
038 Jne,s B4 P=0.000000 C=19097.000000
038
03a B2: # B3 <- B1 Freq: 1
03a XOR EAX,EAX
03c
03c B3: # N1 <- B2 B4 Freq: 1
03c ADD ESP,8 # Destroy frame
POPL EBP
TEST PollPage,EAX ! Poll Safepoint
046 RET
046
047 B4: # B3 <- B1 Freq: 4.76837e-07
047 MOV EAX,#1
04c JMP,s B3
04c
This from the following code:
private static boolean subtractionOverflowed(long original, long
other, long result) {
return (~(original ^ ~other) & (original ^ result) & SIGN_BIT) != 0;
}
So I guess the question comes down to whether this overflow check plus
the associated branching logic is less costly than always using
BigInteger and paying the allocation costs (not to mention whatever
internal costs it has versus doing "long op long", even if you have to
unbox and box on the way in and out.
In JRuby, RubyFixnum has that long field plus an int flags field and a
reference to metaclass, for an additional 8-12 bytes. Narrows the gap
a bit.
I wasn't able to get +PrintAssembly to work (see my other email) so I
wasn't able to see how 32 and 64-bit Hotspot eventually assembles the
above opto assembly. It may be even tighter.
- Charlie
--
You received this message because you are subscribed to the Google Groups "JVM
Languages" group.
To post to this group, send email to [email protected].
To unsubscribe from this group, send email to
[email protected].
For more options, visit this group at
http://groups.google.com/group/jvm-languages?hl=en.