"now"? I've been running on 64 bit hardware and software since 1980.Dann Corbit wrote:Now that 64 bit operating systems and compilers are available, I don't think there is any doubt.Jan Brouwer wrote:Is this a commonly held opinion?bob wrote:Bitboards are the obvious future of computer chess.
I am considering rewriting my engine and I can't make up my mind which way to go (at present I use a mailbox representation).
Need some help on speed optimization
Moderator: Ras
-
bob
- Posts: 20943
- Joined: Mon Feb 27, 2006 7:30 pm
- Location: Birmingham, AL
Re: Need some help on speed optimization
-
sje
- Posts: 4675
- Joined: Mon Mar 13, 2006 7:43 pm
Bitboard vs offset (mailbox)
I don't think that the learning curve is that steep. Any real adaptation slowness is caused by the desire to squeeze out all possible inefficiencies with the details of low level bitboard realization. Much is gained just by having a simplistic bitboard implementation at first and deferring rotated bitboards, magic bitboards, etc., until later.bob wrote:Look at the programs that have switched, from Rybka on down. It is not easy to switch without a rewrite, because now you think "sets" (bitmaps) rather than array loops. This takes some getting used to, so the learning curve is pretty slow. But eventually, you "see the light" and it works very well.
A good bitboard implementation will rid a program of a full level of looping in many, many places and this is a big payoff. But an even bigger payoff is that the use of bitboards provides a higher level of abstraction that makes programming easier.
Having a 64 bit processor is nice, but it's not strictly necessary. The new CIL toolkit uses a bitboard representation of a vector of four 16 bit integers, and that's faster than an offset (mailbox) approach in my tests. Using 32 bit or 64 bit values would be faster if such were well supported on the existing free Common Lisp environments. The implementation could be changed at the lowest level with all changes confined to a single source file if needed.
-
bob
- Posts: 20943
- Joined: Mon Feb 27, 2006 7:30 pm
- Location: Birmingham, AL
Re: Bitboard vs offset (mailbox)
OK, I'll rephrase my statement.sje wrote:I don't think that the learning curve is that steep. Any real adaptation slowness is caused by the desire to squeeze out all possible inefficiencies with the details of low level bitboard realization. Much is gained just by having a simplistic bitboard implementation at first and deferring rotated bitboards, magic bitboards, etc., until later.bob wrote:Look at the programs that have switched, from Rybka on down. It is not easy to switch without a rewrite, because now you think "sets" (bitmaps) rather than array loops. This takes some getting used to, so the learning curve is pretty slow. But eventually, you "see the light" and it works very well.
A good bitboard implementation will rid a program of a full level of looping in many, many places and this is a big payoff. But an even bigger payoff is that the use of bitboards provides a higher level of abstraction that makes programming easier.
Having a 64 bit processor is nice, but it's not strictly necessary. The new CIL toolkit uses a bitboard representation of a vector of four 16 bit integers, and that's faster than an offset (mailbox) approach in my tests. Using 32 bit or 64 bit values would be faster if such were well supported on the existing free Common Lisp environments. The implementation could be changed at the lowest level with all changes confined to a single source file if needed.
If all you want to do is produce a crappy implementation of a chess engine, using bitboards, then there might not be a lot of "learning curve" issues. IF that is all you want to do. If you want to produce a _good_ implementation, then you have to change from thinking loops to thinking sets. I have done both types of programs. _extensively_. Code to do cute things like generate checks or check evasions is _far_ different when written efficiently. The evaluation code is _far_ different. Some do mobility. There are several ways to do this, one way is "bitboardy" the other way(s) are "loopy" no matter which representation you use.
My comments are almost never directed to the person that will be happy with "a crappy implementation." To do it _right_ takes some learning, and the curve was definitely there, at least for me. And I'm hardly a lousy programmer myself.
-
Tord Romstad
- Posts: 1808
- Joined: Wed Mar 08, 2006 9:19 pm
- Location: Oslo, Norway
Re: Need some help on speed optimization
Generating checks would be different, yes. It is a good example of the little low-level components of a chess program which is likely to depend heavily on the board representation. Regardless of the board representation, however, a generator for checking moves is easy to write, and easy to replace.bob wrote:I disagree. The way you use bitboards is _completely_ different than the way you use mailbox representation. With bitboards, you think "sets of squares" whereas with mailbox, you think "looping over a group of squares". The way you think about things is totally different. From generating checks (generate all moves and then cull non-checks to finding sets of squares that can give check and see if you can find a correct piece with a set of attack squares that intersects with the set of targets.
That's precisely why I prefer to always optimize for flexibility. I don't care if all my functions are as fast as technically possible; but I do care that if any part of my program turns out to be too slow, I can easily replace it without massive changes throughout the program.Different philosophies. I don't go for speed "up front". But I try to never design with blinders on so that I make decisions that will make it very difficult to go fast later. That is an easy mistake to make, and you end up rewriting things that could have been designed differently up front.
I evaluate mobility in precisely the same way both with and without bitboards: I count the number of attacked squares not occupied by friendly pieces. Of course, exactly how the counting is done depends on the board representation, but it is hardly an interesting or important part of the program, and is easily hidden in a little low-level function. In the main evaluation function, I thinkFor chess-related stuff, yes. But for things like evaluating mobility, or generating just checks, or generating just legal check evasions, it is a different thing.
Code: Select all
score += RookMobilityWeight * board.rook_mobility(square);Code: Select all
score += RookMobilityWeight * count_1s(board.rook_attacks(square) & ~friendlyPieces);I concede that there is currently a considerable amount of direct bitboard manipulation in my evaluation function, but it doesn't have to be that way.Ditto for evaluation.
I consider this to be one of the main weaknesses of my program.
That's the kind of thing I never want to do, at least not if it ties me to one specific board representation. I have a general dislike for clever-looking code, unless it can be hidden behind some convenient abstraction.You can ask multiple questions with a single AND if you know how to do it and plan on doing that from the beginning.
Without doubt, this difference in philosophy is one of the main reasons why your program is faster than mine, but it is also the reason my program isn't that much weaker, despite being developed with a tiny fraction of the development and testing time, and by a vastly inferior programmer. Writing code in such a way that the board representation becomes unimportant and invisible makes it possible even for mediocre programmers to write a decent chess program.
Nobody can argue against your successes, so your approach surely works for you. However, I am sure it wouldn't work for the average programmer. Very few programmers could have written Crafty. Almost anyone could have written Glaurung.
Tord
-
Tord Romstad
- Posts: 1808
- Joined: Wed Mar 08, 2006 9:19 pm
- Location: Oslo, Norway
Re: Bitboard vs offset (mailbox)
Have you tried wrapping the arithmetic operations inside a logand with #xFFFFFFFF (or #xFFFFFFFFFFFFFFFF for 64-bit operations)? Most good Common Lisp compilers should be able to optimize this into native 32-bit or 64-bit arithmetics. I don't know most of the free Lisps, but I know that at least SBCL does this for 32-bit arithmetics. I am not sure whether there is a 64-bit SBCL yet.sje wrote:Using 32 bit or 64 bit values would be faster if such were well supported on the existing free Common Lisp environments. The implementation could be changed at the lowest level with all changes confined to a single source file if needed.
Of course, writing (logand ... #xFFFFFFFF) everywhere in your code would be annoying, but you should probably be able to hide it with some clever macrology. You could, for instance, try to create a with-64bit-arithmetics macro which walks through a block of code and rewrites all arithmetic expressions using the logand trick.
Tord
-
bob
- Posts: 20943
- Joined: Mon Feb 27, 2006 7:30 pm
- Location: Birmingham, AL
Re: Need some help on speed optimization
I would disagree _strongly_ with both of those last statements.Tord Romstad wrote:Generating checks would be different, yes. It is a good example of the little low-level components of a chess program which is likely to depend heavily on the board representation. Regardless of the board representation, however, a generator for checking moves is easy to write, and easy to replace.bob wrote:I disagree. The way you use bitboards is _completely_ different than the way you use mailbox representation. With bitboards, you think "sets of squares" whereas with mailbox, you think "looping over a group of squares". The way you think about things is totally different. From generating checks (generate all moves and then cull non-checks to finding sets of squares that can give check and see if you can find a correct piece with a set of attack squares that intersects with the set of targets.
That's precisely why I prefer to always optimize for flexibility. I don't care if all my functions are as fast as technically possible; but I do care that if any part of my program turns out to be too slow, I can easily replace it without massive changes throughout the program.Different philosophies. I don't go for speed "up front". But I try to never design with blinders on so that I make decisions that will make it very difficult to go fast later. That is an easy mistake to make, and you end up rewriting things that could have been designed differently up front.
I evaluate mobility in precisely the same way both with and without bitboards: I count the number of attacked squares not occupied by friendly pieces. Of course, exactly how the counting is done depends on the board representation, but it is hardly an interesting or important part of the program, and is easily hidden in a little low-level function. In the main evaluation function, I thinkFor chess-related stuff, yes. But for things like evaluating mobility, or generating just checks, or generating just legal check evasions, it is a different thing.is both more readable and more flexible thanCode: Select all
score += RookMobilityWeight * board.rook_mobility(square);Generating checks and check evasions, as mentioned before, are just semi-trivial low-level which are easy to write and easy to replace, regardless of the board representation.Code: Select all
score += RookMobilityWeight * count_1s(board.rook_attacks(square) & ~friendlyPieces);
I concede that there is currently a considerable amount of direct bitboard manipulation in my evaluation function, but it doesn't have to be that way.Ditto for evaluation.
I consider this to be one of the main weaknesses of my program.
That's the kind of thing I never want to do, at least not if it ties me to one specific board representation. I have a general dislike for clever-looking code, unless it can be hidden behind some convenient abstraction.You can ask multiple questions with a single AND if you know how to do it and plan on doing that from the beginning.
Without doubt, this difference in philosophy is one of the main reasons why your program is faster than mine, but it is also the reason my program isn't that much weaker, despite being developed with a tiny fraction of the development and testing time, and by a vastly inferior programmer. Writing code in such a way that the board representation becomes unimportant and invisible makes it possible even for mediocre programmers to write a decent chess program.
Nobody can argue against your successes, so your approach surely works for you. However, I am sure it wouldn't work for the average programmer. Very few programmers could have written Crafty. Almost anyone could have written Glaurung.
Tord
I doubt many could write code as strong as G2 either. I test against it all the time. Using books, Crafty does "OK" against glaurung. Using my test positions, it does less "OK" but primarily because that forces us to play positions neither of us would normally choose to play, and differences in the two programs can be exaggerated. I'm working on that as we speak.
But I have rewritten my programs too many times. And each time it was because of a poor decision made early-on that dictated major changes to get around some sort of performance bottleneck for a desired new feature. I have not rewritten Crafty nearly as many times, perhaps 2-3 including this most recent black/white elimination. That was the result of just not thinking about efficient ways to do things without the duplication, and it led to code that was extraordinarily hard to modify without introducing symmetry bugs. Had I thought more about the issue before starting to write code, I could have even avoided that rewrite.
-
sje
- Posts: 4675
- Joined: Mon Mar 13, 2006 7:43 pm
Re: Bitboard vs offset (mailbox)
The problem here is that any use of a literal integer value outside the fixnum bounds will contagiously promote all operations into bignum territory, at least on the platforms I'm using. Further, the fixnum limit is only 24 bits for some environments.Tord Romstad wrote:Have you tried wrapping the arithmetic operations inside a logand with #xFFFFFFFF (or #xFFFFFFFFFFFFFFFF for 64-bit operations)? Most good Common Lisp compilers should be able to optimize this into native 32-bit or 64-bit arithmetics.sje wrote:Using 32 bit or 64 bit values would be faster if such were well supported on the existing free Common Lisp environments. The implementation could be changed at the lowest level with all changes confined to a single source file if needed.
The better solution would be to use simple bit vectors throughout, except that my tests showed significant lossage due to poor implementations. I guess that having fast simple bit vector operations is not a high priority among Lisp compiler writers.
-
Zach Wegner
- Posts: 1922
- Joined: Thu Mar 09, 2006 12:51 am
- Location: Earth
Re: Need some help on speed optimization
Hello Tord,
I wouldn't necessarily consider myself a low-level guy. Lower than you, but that's not saying much. Probably about a third of my program would have to be radically changed or rewritten. I don't know if you would consider that difficult, but it's certainly a pain. I have changed representations a few times (I used to use bitboards+0x88, and I've used several different bitboard representations through the years), and while it usually took less than a day IIRC, it wasn't fun. Converting to mailbox would be even harder. The entire move generation, in check, evaluation, make/unmake, and several other smaller functions need a complete rewrite. I guess my issue with it is not that it's hard, but that this code is for the most tedious to write by far. I like experimenting with move generation ideas, but actually writing the move generator is completely boring. Same with make/unmake.
I think if you can convert said functions from bitboard to mailbox without substantially rewriting them, then you have abstracted too much. OTOH, considering how slow and stupid you always say your program is, you need to keep in mind that your engine is still significantly faster than mine in NPS despite having a much larger (and smarter) eval. So that abstraction might not be so bad after all.
Zach
Well I'm going to have to disagree here too.Tord Romstad wrote:Surprisingly, I find myself to disagree with almost everything in Bob's last two posts, probably because I am a high-level guy while Bob is a low-level guy.
I think if you can convert said functions from bitboard to mailbox without substantially rewriting them, then you have abstracted too much. OTOH, considering how slow and stupid you always say your program is, you need to keep in mind that your engine is still significantly faster than mine in NPS despite having a much larger (and smarter) eval. So that abstraction might not be so bad after all.
Zach