Martin Fierz wrote:aloha!
1) my checkers program uses 64-bit hash records. if i understand bob's paper right, then on a 64-bit system, a hashtable read or write would be "atomic", i.e. on such systems i wouldn't have to care about hashing and locks at all?
Lockless hashing (as described by Hyatt) is just a clever trick to make sure the "value" corresponds to the "key". In his implementation a HashEntry is composed of both a 64 bit key and a 64 bit value. So, there is a risk that some other thread will change, say, the "value" right in the middle of your read (after you've read the key but before you've read the value).
If you are using just a single 64 bit entry, and it's read in a single atomic operation, then no - you wouldn't need to deal with that issue. (Side question: Are you storing any additional bits of your key in that 64 bit word?)
Martin Fierz wrote:
2) there is another paper on bob's site on DTS, while tord seems to be using YBW in viper. is there any reason for choosing one or the other in your experience? in particular, which is easiest to implement?
YBWC is much, much easier to implement. I was surprised to learn that not even Bob is really doing DTS. YBW isn't quite as flexible, and I believe it's a different "philosophy" altogether. In the YBW implementations I have seen (Crafty and Viper), once a node searches the "eldest brother", or the first move, it attempts a split. It will succeed if a some processor is idle, and maybe some other criteria are met. But basically, the processors just sit and wait until they are given work to do. Once they are given work, they help their "master" search the remaining moves of the node it's currently working on.
Conversely, in DTS the processors actively look for work when they become idle. All of the busy processors will report some state information, and the idle processor has to decide where to jump in and help. The really interesting thing is that it might decide to help some processor that is currently at ply 10 by splitting at ply 5. So, there is some real "decision making" in DTS.
Most chess programs use a recursive search, and with YBW that is fine as you only split "deeper." WIth DTS on the other hand, you really need an iterative search, since you can split way up the tree.
I think in the future you will see more chess programs using true DTS. At least, that's my gut feeling. I think YBW is sufficient for few processors, but little inefficiencies add up in exponential trees. I think when we start seeing 8 or 16 processor desktops become common place the DTS algorithm will become more common place.
I am still working on implementing an iterative search. My goal is to implement DTS, then to employ a neural net to do the decision making. But, I've a long way to go. I know of at least two others that have done or are working on DTS: the Loop Team and Pradu (Buzz).
Martin Fierz wrote:
3) in checkers, we have huge endgame databases, which are very important for the playing strength (very different than in chess). the endgame databases are too big to fit in the computer's RAM, and therefore we load and unload blocks of the database during the search into the database cache. i am using 1K-sized blocks. obviously, i need to keep track of this cache somehow: every block has a unique number assigned to it, and i have an array which tells me which slot of the database cache is holding which block. whenever a block is loaded, i need to update that. i also have a doubly-linked-list to implement a LRU (least recently used) caching scheme, that is, when a block has to be loaded, i can look up which one of my N blocks was not used for the longest time, then i throw it out. to keep track of this list, i also need to update it every time a block is used which is in memory. so i have:
dblookup with block in memory:
- find out in which block the current position is
- find out where the block is in memory
- look up the current position in the block
- move this block to the front of the LRU-list
dblookup with block on disk
- find out in which block the current position is
- find out that the block is not in memory
- find out which block has not been used for the longest time
- replace that block with the one i want to use
- update the index array which tells me which block is where
- move the block to the front of the LRU-list
compared to hashing, this seems like a much bigger problem to make thread-safe.... my experience with the locks (EnterCriticalSection, LeaveCriticalSection) has been very bad, i.e. they slow down the search a lot. is there anything else that i can do about this database lookup? in particular, how are you handling your database lookups in chess?
cheers
martin