That is a really lousy design however. I can think of no valid reason to compute the hash index at the start of the search, then use that hash index at the end of that ply to do a store. It would work if you use buckets, since when you do the store you have to search through the bucket to find the best entry to replace.xmas79 wrote:I was referring to something like (pseudo):bob wrote:What does buckets have to do with this in a non-threaded search? WHO is going to change the bucket between when you compute the bucket address and when you decide to access an entry within the bucket? The alpha/beta search is a sequential process when only using one thread.
After the use of ptr in hash move phase there is a search, and it can be very deep, allowing for the TT to be "trashed". With this logic in mind, here's an example:Code: Select all
search(a,b) { score = a; ptr = hashProbe(); TryCutoffByUsingPointer(ptr); TryHashMoveByUsingPointer(ptr); ForAllMoves { Make(); score = -search(b,a) UnMake(); if (score > b) { StoreHashByUsingPointer(ptr); return score; } } if (alphaImproved ) { StoreHashByUsingPointer(ptr); } return score; }
Suppose we have 4 buckets per hash slot and suppose 64k entries (0xffff is the key hash mask), it can happen that we get a pointer for the hash key 0x12340000, and we get the hash index 0, so buckets 0, 1, 2, 3, corresponding to memory addresses 0, 16, 32, 48 (eg 16 bytes per entry). Suppose the TT is empty, we stores the hash key 0x12340000 at address 0. Then the search goes (among others) through positions 0xfa020000, 0x12fe0000, 0x16930000, 0x98af0000, 0xffb10000 etc... and then again 0x12340000. All these positions will map to hash index 0, however they will go in different buckets. Eg 0xfa020000 in bucket 1, 0x12fe0000 in bucket 2, 0x16930000 in bucket 3. Now 0x98af0000 and 0xffb10000 must be stored and they will eventually replace the entry 0x12340000 in bucket 0. When the position with hash 0x12340000 will be encountered again, it may be stored at a different bucket, so the pointer to the old hash entry is no longer valid.
Hope it was clear what I meant.
But the bottom line is this: Don't write code that has so many ways to break it that you are guaranteed to encounter one or more of them as time progresses. Computing the hash address takes zero time (typically a simple 1/2 cycle AND operation). There's no reason to do that far away from where you need it, and there are good reasons for doing it right when you need it, namely avoiding a memory reference to fetch the pointer you calculated so long ago.
The time consuming part of the hash probe/store is the memory access for the table entry(s) (or a bucket of them). Moving the address calculations far away from where it is used really makes no sense at all, and just invites bugs. The type of bugs one sees in assembly language where you do a load long before you need it to avoid the latency, then forget about doing it and use that register for something else, which destroys the value you pre-loaded.
BTW you are using a non-traditional definition of bucket. The usual idea is a bucket contains N distinct hash entries. You map to a specific bucket, then choose the best entry from within that bucket. In your explanation, it would work just fine, because a pointer to a bucket doesn't identify the specific entry within that bucket you want...