Ah, I didn’t realize the headers were that much larger on the 64-bit JVM, nor 
did I realize that even small arrays had a size slot.

In theory we could introduce SingletonTupleDescriptor that didn’t bother with a 
hash slot.  When there are no integer slots, a dedicated empty array stored in 
a static is used for the integer slots.  Likewise for the object slots.  So in 
that case we would have 96 - 24 = 72 bytes for the tuple.  The hash value could 
just be calculated on demand.  It would only be a significant slowdown when 
hashing constructs like <<<<<<<<<<foo>>>>>>>>>>.

There are also all sorts of other tricks we could do, like introducing a 
dedicated field in the descriptor itself.  We do that for POJOs (plain old Java 
objects) in Avail.  That requires a separate instance of the descriptor for 
each POJO, however, and that would probably be significantly more expensive 
than the existing tuple implementation.

Oh, and if we do a hash-less SingletonTupleDescriptor in the 64-bit LLVM 
version we should be able to shave it down to just the object pointer and 
header, for 16 bytes (8 bytes for 32-bit).  Going below that would get /very/ 
tricky.


> On Jan 5, 2015, at 4:30 PM, Robbert van Dalen <[email protected]> 
> wrote:
> 
> hi,
> 
> regarding the memory consumption of a tuple containing 1 object reference, i 
> believe the following to be (more) accurate:
> 
> 1. the tuple Avail object:
>  -1 object header @ 8 bytes (klass pointer + monitor + gc bits).
> - 3 slots in the AvailObject itself (descriptor ref, objectSlots ref, 
> intSlots ref) @ 4 bytes
> * total of 24 bytes (padded)
> 
> but… we also need to take the descriptor object, integer array and object 
> array into account:
> 
> 2. the descriptor object:
>  … are shared and globally allocated once - so not taken into account.
> 
> 3. the integer array object containing 1 int (the hash)
>  - 1 object header @ 8 bytes
>  - 1 size slot @ 4 bytes
>  - 1 int @ 4 bytes
>  * total of 16 bytes *
> 
> 4. the object array object containing 1 object reference
> - 1 object header @ 8 bytes
> - 1 size slot @ 4 bytes
> - 1 object reference @ 4 bytes
> * total of 16 bytes *
> 
> a total of 56 bytes on a 32 bits jvm.
> 
> on a 64 bit jvm, that figure is going to be obviously worse: uncompressed, 
> every object header goes from 8 to 16 bytes and every object reference goes 
> from 4 to 8 bytes:
> 
> 1. tuple object (64 bit, uncompressed): 40 bytes
> 2. integer array (64 bit, uncompressed): 24 bytes
> 3. object array (64 bit, uncompressed): 32 bytes (padded).
> 
> a total of 96 bytes o a 64 bit jvm - uncompressed.
> 
> compressed, every object header goes from 8 to 12 bytes and every object 
> reference will stay 4 bytes (if the heap stay below 4 gig).
> 
> 1. tuple object (64 bit, compressed): 24 bytes
> 2. integer array (64 bit, compressed): 24 bytes (padded).
> 3. object array (64 bit, compressed): 24 bytes (padded).
> 
> a total of 72 bytes on a 64 bit jvm - compressed.
> that’s 3 times as big as a regular java object array containing one object 
> reference.
> 
> i’m sure the llvm memory representation will be much better.
> 
> cheers,
> - robbert
> 
>> Like all AvailObjects, a tuple has the usual Java header and two slots, one 
>> for the array of AvailObjects and one for the array of ints.  An ordinary 
>> tuple (not one containing, say, a character or a nybble) has one object slot 
>> and one integer slot, so both arrays are necessary.  When there are zero 
>> object slots or integer slots in an AvailObject, the single shared empty 
>> array of objects or the single shared empty array of ints is used instead of 
>> creating one especially for that AvailObject.  However, since we need one 
>> slot of each for the one-element ordinary tuple (whose descriptor is an 
>> ObjectTupleDescriptor), we have:
>> 
>>      3 object headers (AvailObject, AvailObject [], int []) @ 8 bytes each,
>>      3 slots in the AvailObject itself (descriptor, objectSlots, intSlots) @ 
>> 8 bytes each
>>      1 slot in the int [] @ 4 bytes (to cache the tuple's hash value)
>>      1 slot in the AvailObject [] @ 8 bytes (the tuple element)
>> 
>> So a one-element tuple takes 60 bytes.  On the other hand, the zero element 
>> tuple is shared and takes no additional space per use (the slot holding it 
>> would be accounted with whatever structure owns the slot).
>> 
>> Compressed pointers would shave it to 44 bytes.  The custom representation 
>> we'll be using with LLVM will look like:
>> 
>>      ...before the object...
>>      [tuple element #1] @ 8 bytes
>>      [middle-header] @ 8 bytes
>>      [int slot #1 for hash] @ 4 bytes
>>      ...after the object...
>> 
>> = 20 bytes.
> 

Attachment: signature.asc
Description: Message signed with OpenPGP using GPGMail

Reply via email to