Re: [gui-dev] QueryRouteTable.java.patch (second version)

"Philippe Verdy" <[email protected]> Mon, 29 Nov 2004 22:04:46 +0100
Newsgroups gmane.network.gnutella.limewire.gui.devel
Message-ID <[email protected]>
From: "Zlatin Balevsky" <[email protected]>
> A small note on RLE - we could relatively easily modify our table reading 
> code to read directly from a RLE'd table; this alone could save us a lot 
> of memory on the leaf tables.  This seems like the easiest first step to 
> take.

It's just that I really doubt about the efficiency of RLE encoding for 
bitsets filled by QRP, whose set bits are more or less regularly distributed 
throughout the table.
Same thing about the tree encoding, for the same reason, because the tree 
structure itself will be hard and costly to compute on many levels, and a 
limited number of levels will necessarily have almost all branches filled 
with an actual node, at very moderate fill level.
For example, with a 64Kbit table, a 5% fill level means that there's one bit 
set every 20 bit (so the RLE compression will not be extremely performing, 
due to very short compressible lengths, and 20 bits will not be enough to 
avoid that all branches in a tree representation, where leaf nodes will need 
to represent some significant group of bits, will be present).

What is interesting to see first, is how many bits are set in leaf nodes 
(those for which we need the highest number of QRP tables, and so the 
highest number of large bitsets). On average it should represent 2 or 3 
times the number of shared files on each leaf node. If we expect (and 
measure) that on average, leaf nodes share about 300 files, this will set 
about 1000 bits in their bitset. To start being significantly compressible, 
a large bitset should be about 50 times this number, so this gives an 
initial bitset size of at least 50,000 bits; the current size of 64K bits is 
then very moderately compressible with RLE (I think we could gain no more 
than 10 to 20% of total size for 64K bitsets, i.e. a total size of about 80 
to 90% of an uncompressed bitset).

But if you double the size of the virtual bitset, you don't modify the 
number of total bits set, so the compression rate will be significantly 
higher, because all additional bits will be zeroes, so that the compressed 
data could be kept roughly at the same size as a 64Kbit table, effectively 
more than doubling the compression rate: suppose as above that we compressed 
the 64Kbit size at 80 to 90%, then the comrpessed data would be around 51 to 
57 Kbit; with a doubled virtual table size of 128Kbit, the compressed data 
would remain roughly at the same size of 51 to 57 Kbit, i.e. a compressed 
size at 40 to 45% of the uncompressed bitset.

The more we increase the virtual bitsize, the more the RLE compression will 
become effective to maintain (or not increase significantly the average 
compressed size).

The problem with this scheme is in its maintenance cost: now the compressed 
bitsets have dynamic size, so we need to reallocate very often each time a 
bitset needs to be updated. This can cause significant performance penalties 
due to excessive fragmentation, if we don't split the bitsets into fixed 
virtual size (I think that fragments around 16Kbits, i.e. a max of 4KB, 
should work OK to greatly limit the memory fragmentation costs in the VM.) 
Splitting fragments may also offer another opportunity to simply not store 
completely empty fragments, because their position in a table of fragments 
will be left to null, adding some memory save. Splitting is very simple to 
compute with simple bitmasks and shifts, and it limits a lot the impact of 
necessary data copies and allocations.

Also each fragment may have its own compression scheme, if fragments are 
represented as distinct objects; no need to add other fields because their 
instance's class pointer wil implement a common interface to effectively 
decode them; this allows also to temporarily compute fragments one by one in 
an uncompressed form, easy to compute and set for OR/XOR/AND operations, and 
terminate this work by computing their optimal compression scheme, before 
going to the next fragment. The temporary uncompressed fragment is easily 
reusable to compute the next actual fragment, so this limits the number of 
allocations needed to process the whole bitset.


_______________________________________________
gui-dev mailing list
[email protected]
http://www.limewire.org/mailman/listinfo/gui-dev