hgm wrote:bob wrote:Ever consider that perhaps your idea will _not_ work? If you aren't willing to listen, blunder ahead until you see the light...
Of course I considered that, I consider it all the time. But not being able to split at a split point because spliiting at split points in all cases will cause a disastrous drop in performance will certainly not be a failure mode I would consider...
I have no idea what that means. There are well-known (sane) ways to split a tree, from the old principle-variation-split algorithm used by some of us in the 1980 time-frame, to more sophisticated approaches like DTS used in the late 80's and beyond. There are well-known bad ways to split also, and using the hash table is one of them. It sort of works for 2 processors, it is bad for 4, and it is simply lousy beyond 4.
And you have _yet_ to answer this problem with repetitions, which I will clearly define so we can get beyond this point...
one process searches starting at node A and continues for several plies until it reaches node B. It then continues searching beyond that node eventually hitting node C. Meanwhile another process decides to help this process out, whether it be via a shared hash table, a traditional split approach or whatever, and it decides to help this guy out starting at node B. Now you have to solve the following problem, with 100% accuracy.
Whenever the original process does a repetition check, it has to check against
the positions between A to B to whatever position it currently is on. It can not check any positions that are not part of its own subtree below node B. So, simply stated, it must search the global set of positions from A to B, and the private set of positions below B since another processor is also searching below B and would be searching different positions that would not be a repetition on our branch.
the second process has started to search at B, and continues on to D. When it does a repetition check, it must check against positions between A and B (that are common with the other process) and then against positions between B and D (that are _not_ in common with the other process.
How are you going to handle that? In my case, if I split at B, I just duplicate the repetition list that covers A-B, and then let the splitting process add stuff to that and no other process can see the added stuff. This guarantees 100% accuracy in repetitions, with very little overhead (at a typical split point, I copy just one entry because I use the same idea that is used in the RepetitionCheck() code, in that there is no need to copy early positions made prior to an irreversible move. In a hash approach, you can either duplicate the entire table to provide correctness, or each entry has to be tagged with the process ID it belongs to so that two different processes will not match each other's positions and get false repetitions. And you have to handle the A-B case where _both_ processors can match those positions.
As I said, it is far messier than you are suspecting, but you actually have to write a parallel search before you begin to see the hidden issues. My first parallel search played in the 1978 ACM computer chess tournament running on a Univac 1100/42 (dual cpu machine). Yours has yet to be designed, much less written. I suspect my experience here provides the better insight into the pitfalls lying ahead...
But go as you please... it is your time you are wasting...
You hash to a zero entry. I execute a loop one iteration. And your approach is supposedly faster? Please think again. How do you know your entry is zero? Perhaps a comparison? I do a single comparison to determine that the hash signature does not match the table entry. A single comparison is a single comparison. And I don't have to do a store to zero the entry when I exit, I just use a counter that is decremented for other reasons so there is no cost in doing it associated with repetitions.
And how do you know the element you compared with was the last one? Another comparison, perhaps?
Perhaps a loop? Did you not use a loop? Do you have a more efficient way of doing a loop than I do? Say one without a test and branch?
didn't think so.
Perhaps your usage of those silly emoticons is a better way of spending your time than having a technical discussion, since you are just not wanting to get the point.
As I don't waste time on updating a fifty-move counter for other purposes, for me the fact that it has to decrement after UnMake would cancel out the fact that I have to clear the entry (which doesn't require a new lookup, as the address is already known). And even worse, the 50-move counter would also have to be incremented on MakeMove. So that would also be twice as expensive as what I do now. Even worser, the 50-move counter would have to be _conditionally_ incremented, making it far, far more expensive.
That's stupid. If you don't want to implement all the rules of chess, that's fine. But that is not a point in your favor when comparing effort. The 50-move-rule is necessary in a chess engine if it is going to compete with IM/GM level players. If that is not your goal, dump the repetition check completely as it is also just as big a waste of time. If you are going to implement something half-assed, then don't try to compare it to something that works correctly. What I do is correct. What you do is a hack that works part-time. Uri pointed that out. As did I. So get off that discussion because if you ever decide to develop something truly competitive, you will also be handling the 50 move rule counter, and that difference between our programs goes away.
While you are at it, you might eliminate under-promotions as well. That will also make you faster.
Boy do these discussions go South quickly. I do something right, you choose to omit it, and then you claim that my doing it right makes my repetition code less efficient than yours???