flok wrote: ↑Fri Jun 11, 2021 4:11 pmWell, my program is using a kind of perft divide function, so it prints out all the moves and their node counts at the depth it's run at. And no, I'm not entirely sure about the discrepancy between real and cpu-time. I'll have to do some digging.algerbrex wrote: ↑Fri Jun 11, 2021 5:06 amAny idea why real is so much longer (about 20 seconds) than the cpu-time for user and system combined?Code: Select all
Total nodes: 4085603 real 1m53.013s user 1m18.742s sys 0m15.138s
Also: 15 seconds for system? Is your program causing swapping? Or is it emitting a lot of text to the terminal?
Advice on optimizing my move generation
Moderator: Ras
- 
				algerbrex  
- Posts: 608
- Joined: Sun May 30, 2021 5:03 am
- Location: United States
- Full name: Christian Dean
Re: Advice on optimizing my move generation
- 
				JVMerlino
- Posts: 1404
- Joined: Wed Mar 08, 2006 10:15 pm
- Location: San Francisco, California
Re: Advice on optimizing my move generation
As others have said, getting a decent engine doesn't require a super-fast move generator. Once you have a strong search and reasonably complex evaluation, the move generation will only take a small percentage of overall search time. So spending a lot of time optimizing it will only result in a very small increase in strength.
So don't be disappointed about your current performance. I just ran Myrddin on the kiwipete position, perft 6, WITH bulk counting (but no hash), and it took 3 minutes.
			
			
									
						
										
						So don't be disappointed about your current performance. I just ran Myrddin on the kiwipete position, perft 6, WITH bulk counting (but no hash), and it took 3 minutes.

- 
				algerbrex  
- Posts: 608
- Joined: Sun May 30, 2021 5:03 am
- Location: United States
- Full name: Christian Dean
Re: Advice on optimizing my move generation
Yeah, that's what I've seen a couple of other people sayJVMerlino wrote: ↑Fri Jun 11, 2021 5:48 pm As others have said, getting a decent engine doesn't require a super-fast move generator. Once you have a strong search and reasonably complex evaluation, the move generation will only take a small percentage of overall search time. So spending a lot of time optimizing it will only result in a very small increase in strength.
So don't be disappointed about your current performance. I just ran Myrddin on the kiwipete position, perft 6, WITH bulk counting (but no hash), and it took 3 minutes.
 , so that's quite encouring. My goal ELO would be anywhere between 2000-1500 ELO, so if my move generator will get me there, I can work with that!
, so that's quite encouring. My goal ELO would be anywhere between 2000-1500 ELO, so if my move generator will get me there, I can work with that!- 
				maksimKorzh  
- Posts: 775
- Joined: Sat Sep 08, 2018 5:37 pm
- Location: Ukraine
- Full name: Maksim Korzh
Re: Advice on optimizing my move generation
Got you, well my engine in Javascript (5x times slower than C because of Javascript itself) runs this position in around 3 seconds:algerbrex wrote: ↑Fri Jun 11, 2021 5:27 pmHi and yes, that's also where I suspect I'm being very inefficient. Right now, I'm detecting if the king is left in check by making a movie using a isSquareAttacked method which does the usual approach of sitting a super-piece on the king square, radiating out rays, and seeing if they hit any enemy pieces. Here is the code:maksimKorzh wrote: ↑Fri Jun 11, 2021 11:06 am How you deal with attacks (king in check detection)?
On the fly generation or lookup?
In my experience attacks detection is the biggest time eater,
some other potential slowdowns might occur because of data structures being used during move generations
like putting moves onto stack or similar.
This is working excellent in terms of being bug free, but I do feel like there are faster ways of check detection. But I couldn't find any good resources for this related to mailbox representations of boards. Everything is related to bitboards, which I've already seen, since I attempted to write my first three engines using those.Code: Select all
func squareIsAttacked(board *Board, square int, whiteToMove bool) bool { enemyColor := White pawnAttackLeftDelta := SouthWestDelta pawnAtttackRightDelta := SouthEastDelta if whiteToMove { enemyColor = Black pawnAttackLeftDelta = NorthWestDelta pawnAtttackRightDelta = NorthEastDelta } // Send out rays from the square position. If any of the rays intersect // with an enemy rook, bishop, or queen, then the square is attacked. for _, delta := range QueenDeltas { to := Mailbox120[Mailbox64[square]+delta] for to != -1 && board.Pieces[to] == NoPiece { to = Mailbox120[Mailbox64[to]+delta] } if delta == NorthDelta || delta == SouthDelta || delta == EastDelta || delta == WestDelta { if to != -1 && board.Colors[to] == enemyColor && (board.Pieces[to] == Rook || board.Pieces[to] == Queen) { return true } } else if delta == NorthEastDelta || delta == SouthEastDelta || delta == NorthWestDelta || delta == SouthWestDelta { if to != -1 && board.Colors[to] == enemyColor && (board.Pieces[to] == Bishop || board.Pieces[to] == Queen) { return true } } } // Send out knight rays from the square position, if they intersect with an enemy knight, // the square is attacked. for _, delta := range KnightDeltas { to := Mailbox120[Mailbox64[square]+delta] if to != -1 && board.Colors[to] == enemyColor && board.Pieces[to] == Knight { return true } } // Lastly, check if the square is attacked by a pawn or a king. for _, delta := range KingDeltas { to := Mailbox120[Mailbox64[square]+delta] if to != -1 && board.Colors[to] == enemyColor && board.Pieces[to] == King { return true } else if delta == pawnAttackLeftDelta || delta == pawnAtttackRightDelta { if to != -1 && board.Colors[to] == enemyColor && board.Pieces[to] == Pawn { return true } } } // If we've made it to here without returing early, the square is not attacked and safe. return false }
Code: Select all
Performance test:
wukong.js:881    move 1:  d5d6     nodes: 79551
wukong.js:881    move 2:  d5e6     nodes: 97464
wukong.js:881    move 3:  a2a3     nodes: 94405
wukong.js:881    move 4:  a2a4     nodes: 90978
wukong.js:881    move 5:  b2b3     nodes: 81066
wukong.js:881    move 6:  g2g3     nodes: 77468
wukong.js:881    move 7:  g2g4     nodes: 75677
wukong.js:881    move 8:  g2h3     nodes: 82759
wukong.js:881    move 9:  e5d3     nodes: 77431
wukong.js:881    move 10: e5g4     nodes: 79912
wukong.js:881    move 11: e5c4     nodes: 77752
wukong.js:881    move 12: e5d7     nodes: 93913
wukong.js:881    move 13: e5f7     nodes: 88799
wukong.js:881    move 14: e5c6     nodes: 83885
wukong.js:881    move 15: e5g6     nodes: 83866
wukong.js:881    move 16: c3d1     nodes: 84782
wukong.js:881    move 17: c3b1     nodes: 84773
wukong.js:881    move 18: c3b5     nodes: 81498
wukong.js:881    move 19: c3a4     nodes: 91447
wukong.js:881    move 20: d2c1     nodes: 83037
wukong.js:881    move 21: d2e3     nodes: 90274
wukong.js:881    move 22: d2f4     nodes: 84869
wukong.js:881    move 23: d2g5     nodes: 87951
wukong.js:881    move 24: d2h6     nodes: 82323
wukong.js:881    move 25: e2d1     nodes: 74963
wukong.js:881    move 26: e2f1     nodes: 88728
wukong.js:881    move 27: e2d3     nodes: 85119
wukong.js:881    move 28: e2c4     nodes: 84835
wukong.js:881    move 29: e2b5     nodes: 79739
wukong.js:881    move 30: e2a6     nodes: 69334
wukong.js:881    move 31: a1b1     nodes: 83348
wukong.js:881    move 32: a1c1     nodes: 83263
wukong.js:881    move 33: a1d1     nodes: 79695
wukong.js:881    move 34: h1g1     nodes: 84876
wukong.js:881    move 35: h1f1     nodes: 81563
wukong.js:881    move 36: f3f4     nodes: 90488
wukong.js:881    move 37: f3f5     nodes: 104992
wukong.js:881    move 38: f3f6     nodes: 77838
wukong.js:881    move 39: f3g3     nodes: 94461
wukong.js:881    move 40: f3h3     nodes: 98524
wukong.js:881    move 41: f3e3     nodes: 92505
wukong.js:881    move 42: f3d3     nodes: 83727
wukong.js:881    move 43: f3g4     nodes: 92037
wukong.js:881    move 44: f3h5     nodes: 95034
wukong.js:881    move 45: e1g1     nodes: 86975
wukong.js:881    move 46: e1c1     nodes: 79803
wukong.js:881    move 47: e1f1     nodes: 77887
wukong.js:881    move 48: e1d1     nodes: 79989
wukong.js:893 
   Depth: 4
   Nodes: 4085603
    Time: 2513 mshttps://maksimkorzh.github.io/wukongJS/wukong.html
to run perft on this position:
1. Click FEN button
2. Open DevTools in the browser (Ctrl-Shift-I or rightclick -> inspect element)
3. Navigate to "Console" tab and type: engine.perft(4) or whatever depth you like
This would run perft in console (I've pasted current output from there)
My engine uses exactly the same is square attacked implementation as yours.
The rest of a movegen is extremely simple and didactic as well (all my engines are didactic))) haha))) )
Here's the source code:
https://github.com/maksimKorzh/wukongJS (Project)
https://github.com/maksimKorzh/wukongJS ... /wukong.js(Source code - 1 file only!)
It can be played as UCI engine as well if hack around a bit.
For this sort of an implementation class this is propbably the fastest implementation available.
I believe that further speedups are tight with redesigning the engine fully or partially (which ruins current "bugfreeness" )
A totally next level many many times faster can be found in HGM's sources:
QPerft (lighting fast perft, around as stockfish or even faster depending on compilation):
https://github.com/maksimKorzh/hgm-mail ... n/qperft.c
The engine from "Mailbox trials thread":
https://github.com/maksimKorzh/hgm-mail ... ailbox7b.c
These to are not recalculating attacks on the fly, see this thread for more details:
http://talkchess.com/forum3/viewtopic.php?f=7&t=76773
Hope it helps!
Didactic chess engines:
https://www.chessprogramming.org/Maksim_Korzh
Chess programming YouTube channel:
https://www.youtube.com/channel/UCB9-pr ... KKqDgXhsMQ
			
						https://www.chessprogramming.org/Maksim_Korzh
Chess programming YouTube channel:
https://www.youtube.com/channel/UCB9-pr ... KKqDgXhsMQ
- 
				emadsen  
- Posts: 441
- Joined: Thu Apr 26, 2012 1:51 am
- Location: Oak Park, IL, USA
- Full name: Erik Madsen
Re: Advice on optimizing my move generation
Hi Christian. Welcome to the chess programming community. Congrats on writing a correct move generator. That's a big milestone! Are you sure it's correct? Here's a good perft test suite that has many corner cases.
In my opinion, you should focus on perft correctness and and search speed. Perft speed is not important for the primary purposes of chess engines: playing games against other engines and aiding us humans in analyzing games. Perft doesn't do any of the "bookkeeping" necessary for good move ordering, which is critical for engine strength. So fast perft doesn't imply strong engine.
I go further than most, in that I've designed my perft method to execute the same call stack as the search method does. So correct perft counts give me confidence search is examining all the moves it should (exempting pruning).
Others have given good advice in previous comments, especially cautioning against allocating memory during search. That kills performance, especially in a garbage collected language like Go. Allocate all data structures when the engine starts. Use statically-sized arrays, not dynamically sized lists (for the same reason, to avoid alloc and GC). Iterate over arrays using for loops, not foreach (for the same reason, avoiding alloc and GC). In C# enumerators alloc. Not sure about Go. Avoid other language features during search that harm performance: no floating point math (instead calc at engine start and store ints in arrays for lookup during search, eval should be pure int math), no lambda methods (they allocate a delegate, at least in C#), etc.
Erik Madsen | My C# chess engine: https://www.madchess.net
			
						- 
				Henk
- Posts: 7251
- Joined: Mon May 27, 2013 10:31 am
Re: Advice on optimizing my move generation
Even if move generation works perfectly it is not that easy to keep it that way. After some time  code changes and being to lazy  or just forget to repeat all perft tests.
			
			
									
						
										
						- 
				mvanthoor  
- Posts: 1784
- Joined: Wed Jul 03, 2019 4:42 pm
- Location: Netherlands
- Full name: Marcel Vanthoor
Re: Advice on optimizing my move generation
Why would the move generator chance "after some time" ?
It is a completely independent part of my engine. Apart from some tiny changes (Rust telling me: "This is going to change in version 2021, the syntax is going to be like this now") and some updates in the comments, the move generator hasn't changed for almost 10 months.
Actually, most of the engine never changes. The biggest changes can be found in the search, and later, the evaluation. Many parts probably won't change for years to come, apart from updating them to current Rust standards, and to call new functionality. If you're constantly having to change things throughout your entire engine when you create new functionality, you've probably made some design mistakes.
- 
				Henk
- Posts: 7251
- Joined: Mon May 27, 2013 10:31 am
Re: Advice on optimizing my move generation
I always have design mistakes even if I don't. My code always changes even if it doesn't have to. 
Code never perfect. Ideas changing all the time.
			
			
									
						
										
						Code never perfect. Ideas changing all the time.
- 
				niel5946
- Posts: 174
- Joined: Thu Nov 26, 2020 10:06 am
- Full name: Niels Abildskov
Re: Advice on optimizing my move generation
An elo below 2000 doesn't require a good move generator at all. I think with a descent evaluation (material and psqt) and a bug-free search, you can definitely get to 1800 with a, relatively, slow move generator.
Additionally, some tricks can be done in search to completely avoid generating some of the moves. Since captures are usually better than quiet moves, you can start by generating the captures and trying them. If they are not good enough, you can then generate the quiets, but if the captures are good enough, you saved time by not generating the rest of the moves. This is called staged move generation. Once you're at 2000, you can look into that and move generator speedup

- 
				JohnWoe
- Posts: 529
- Joined: Sat Mar 02, 2013 11:31 pm
Re: Advice on optimizing my move generation
Perft to me is pointless. Mayhem has no perft and never will. Havoc has it but it's probably going to removed. I can't even remember the time Mayhem had mgen bugs. 
How super fast move generator translates to search speed is very important. Especially in Mayhem I want a list of moves ASAP.
If you make your engine faster that is +Elo. No need to run 500k games. It's simple as that.
In Mayhem the more nodes I search the better hashtable it produces. So that's why super fast move generator is important.
UCI has tons of overhead too. You are starting from "tabula rasa" every move and playing a long list of moves to empty board...
			
			
									
						
										
						How super fast move generator translates to search speed is very important. Especially in Mayhem I want a list of moves ASAP.
If you make your engine faster that is +Elo. No need to run 500k games. It's simple as that.
In Mayhem the more nodes I search the better hashtable it produces. So that's why super fast move generator is important.
UCI has tons of overhead too. You are starting from "tabula rasa" every move and playing a long list of moves to empty board...