On Wed, 23 Jun 2021 at 12:17, Thomas Munro <thomas.mu...@gmail.com> wrote:
>
> David and I discussed this a bit off-list, and I just wanted to share
> how I understand the idea so far in case it helps someone else.  There
> are essentially three subcomponents working together:

Thanks for taking an interest in this. I started looking at your idea
and I've now changed my mind from just not liking it to thinking that
the whole idea is just completely horrible :-(

It gets really messy with all the nested pre-processor stuff around
fetching the element from the segmented array inside simplehash.  One
problem is that simplehash needs the address of the segments despite
simplehash not knowing anything about segments. I've tried to make
that work by passing in the generic hash struct as simplehash's
private_data. This ends up with deeply nested macros all defined in
different files. I pitty the future person debugging that.

There is also a problem of how to reference simplehash functions
inside the generichash code.  It's not possible to do things like
SH_CREATE() because all those macros are undefined at the end of
simplehash.h. It's no good to hardcode the names either as GH_PREFIX
must be used, else it wouldn't be possible to use more than 1
differenrly defined hash table per .c file.  Fixing this means either
modifying simplehash.h to not undefine all the name macros at the end
maybe with SH_NOUNDEF or creating another set of macros to build the
names for the simplehash functions inside the generic hash code. I
don't like either of those ideas.

There are also a bunch of changes / API breakages that need to be done
to make this work with simplehash.h.

1) Since I really need 8-byte buckets in the hash table to make this
as fast as possible, I want to use the array index for the hash status
and that means changing the simplehash API to allow that to work.
This requires something like SH_IS_BUCKET_INUSE, SH_SET_BUCKET_INUSE,
SH_SET_BUCKET_EMPTY.
2) I need to add a new memory allocation function to not zero the
memory.  At the moment all hash buckets are emptied when creating a
table by zeroing the bucket memory. If someone defines
SH_SET_BUCKET_EMPTY to do something that says 0 memory is not empty,
then that won't work. So I need to allocate the bucket memory then
call SH_SET_BUCKET_EMPTY on each bucket.
3) I'll need to replace SH_KEY with something more complex.  Since the
simplehash bucket will just have a uint32 hashvalue and uint32 index,
the hash key is not stored in the bucket, it's stored over in the
segment. I'll need to replace SH_KEY with SH_GETKEY and SH_SETKEY.
These will need to consult the simplehash's private_data so that the
element can be found in the segmented array.

Also, simplehash internally manages when the hash table needs to grow.
I'll need to perform separate checks to see if the segmented array
also must grow.  It's a bit annoying to double up those checks as
they're in a very hot path as they're done everytime someone inserts
into the table.

> 2.  A bitmapset that tracks unused elements in 1, making it easy to
> find the lowest-index hole when looking for a place to put a new one
> by linear search for a 1 bit, so that we tend towards maximum density
> despite having random frees from time to time (seems good, the same
> idea is used in  kernels to allocate the lowest unused file descriptor
> number).

I didn't use Bitmapsets. I wanted the bitmaps to be allocated in the
same chunk of memory as the segments of the array.  Also, because
bitmapset's nwords is variable, then they can't really do any loop
unrolling.  Since in my implementation the number of bitmap words are
known at compile-time, the compiler has the flexibility to do loop
unrolling.  The bitmap manipulation is one of the biggest overheads in
generichash.h. I'd prefer to keep that as fast as possible.

> 3. A hash table that has as elements indexes into 1.  It somehow hides
> the difference between keys (what callers look things up with) and
> keys reachable by following an index into 1 (where elements' keys
> live).

I think that can be done, but it would require looking up the
segmented array twice instead of once.  The first time would be when
we compare the keys after seeing the hash values match. The final time
would be in the calling code to translate the index to the pointer.
Hopefully the compiler would be able to optimize that to a single
lookup.

> One thought is that you could do 1 as a separate component as the
> "primary" data structure, and use a plain old simplehash for 3 as a
> kind of index into it, but use pointers (rather than indexes) to
> objects in 1 as elements.  I don't know if it's better.

Using pointers would double the bucket width on a 64 bit machine. I
don't want to do that. Also, to be able to determine the segment from
the pointer it would require looping over each segment to check if the
pointer belongs there. With the index we can determine the segment
directly with bit-shifting the index.

So, with all that. I really don't think it's a great idea to try and
have this use simplehash.h code. I plan to pursue the idea I proposed
with having seperate hash table code that is coded properly to have
stable pointers into the data rather than trying to contort
simplehash's code into working that way.

David


Reply via email to