Simplifying code
Moderators: hgm, Rebel, chrisw
-
- Posts: 7220
- Joined: Mon May 27, 2013 10:31 am
Re: Simplifying code
I go for simplicity and testability. Factor it by using a wrapper/functor or something like that. So the 'wrapper' should be able to be tested in Isolation. Also code should only depend on interfaces or abstract classes when possible.
-
- Posts: 7220
- Joined: Mon May 27, 2013 10:31 am
Re: Simplifying code
Maybe next step in my engine's code is to hide (bit)board (representation) if it isn't too late for that.
Looks like I violated many good software engineering principles only to win maybe 20% speed.
Abstract datatypes. A concept already known in the eighties. How stupid can one be to ignore that only to win some cpu cycles.
Even functional programming is from the eighties.
I mean my engine is already so slow. It can almost afford anything.
Looks like I violated many good software engineering principles only to win maybe 20% speed.
Abstract datatypes. A concept already known in the eighties. How stupid can one be to ignore that only to win some cpu cycles.
Even functional programming is from the eighties.
I mean my engine is already so slow. It can almost afford anything.
-
- Posts: 1784
- Joined: Wed Jul 03, 2019 4:42 pm
- Location: Netherlands
- Full name: Marcel Vanthoor
Re: Simplifying code
What language are you writing in?
If the engine is slow, then fix that first, after fixing the bugs. In a chess engine, speed is your currency. Because it's slow, you CAN'T afford anything, actually.
When being careful and not haphazardly using any feature I come across, I can just follow programming best practices without speed loss. As you may have seen in the Perft optimization topic, I've been refactoring myself. Case in point: I wanted the move generator out of the board. There are two possibilities to properly do that:
1. Use a global static struct (in Rust, with lazy_static)
2. Build a superstruct that encompasses the smaller components. In this case, it's called "Engine", but in some languages this construction is the default, where such a struct/class is called "App".
I've tried the first option, as it was the easiest. It was implemented in 15 minutes and worked great. Only problem was... lazy_static turned out to be slow. The engine dropped 35% (!) in speed.
Then I tried option 2, create the "Engine" struct. This struct is going to hold all the "global" stuff such as the move generator, evaluation, hash tables... and so on, and the functions to use those things. Then I just throw a board at the struct Engine, with something like this:
Then the engine calls the underlying "real" perft function and provisions it with everything it needs. This way I've been able to refactor the move generator out of the board. It was about half an hour of work (and I still need to move some functions to a more logical place as a result of this refactor), but the engine retained its original speed. If anything, it is a few tens of seconds faster again as far as I can see.
That is what I meant with "haphazardly using functionality". I could have implemented lazy_static, confirmed that it worked, and call it a day; that would have cost me 35% speed. For Rust and my engine, the second solution is a somewhat more complex (but still a best-practice) option, that is much faster.
What I'm trying to say is that refactoring a code base into something that uses good programming practices, doesn't HAVE to cost a huge amount of speed.
If the engine is slow, then fix that first, after fixing the bugs. In a chess engine, speed is your currency. Because it's slow, you CAN'T afford anything, actually.
When being careful and not haphazardly using any feature I come across, I can just follow programming best practices without speed loss. As you may have seen in the Perft optimization topic, I've been refactoring myself. Case in point: I wanted the move generator out of the board. There are two possibilities to properly do that:
1. Use a global static struct (in Rust, with lazy_static)
2. Build a superstruct that encompasses the smaller components. In this case, it's called "Engine", but in some languages this construction is the default, where such a struct/class is called "App".
I've tried the first option, as it was the easiest. It was implemented in 15 minutes and worked great. Only problem was... lazy_static turned out to be slow. The engine dropped 35% (!) in speed.
Then I tried option 2, create the "Engine" struct. This struct is going to hold all the "global" stuff such as the move generator, evaluation, hash tables... and so on, and the functions to use those things. Then I just throw a board at the struct Engine, with something like this:
Code: Select all
engine.perft(&mut board, 6);
That is what I meant with "haphazardly using functionality". I could have implemented lazy_static, confirmed that it worked, and call it a day; that would have cost me 35% speed. For Rust and my engine, the second solution is a somewhat more complex (but still a best-practice) option, that is much faster.
What I'm trying to say is that refactoring a code base into something that uses good programming practices, doesn't HAVE to cost a huge amount of speed.
-
- Posts: 12542
- Joined: Wed Mar 08, 2006 8:57 pm
- Location: Redmond, WA USA
Re: Simplifying code
Don't forget the famous Einstein quote: "Things should be made as simple as possible, but no simpler."
The most important thing for your code is for you to understand it at a glance.
But don't cut functionality to make it simpler, unless it is buggy. In such a case, simplify to the simplest possible base case and work up
The most important thing for your code is for you to understand it at a glance.
But don't cut functionality to make it simpler, unless it is buggy. In such a case, simplify to the simplest possible base case and work up
Taking ideas is not a vice, it is a virtue. We have another word for this. It is called learning.
But sharing ideas is an even greater virtue. We have another word for this. It is called teaching.
But sharing ideas is an even greater virtue. We have another word for this. It is called teaching.
-
- Posts: 7220
- Joined: Mon May 27, 2013 10:31 am
Re: Simplifying code
C# so it does not force you to create a .h file like in C++.
So when in a hurry you omit that. Result bad code.
Looks like hiding bitboard representation difficult. Much code using bitboards.
[Other things to do. Like learning for instance .Net core Entity Framework etc]
-
- Posts: 4052
- Joined: Thu May 15, 2008 9:57 pm
- Location: Berlin, Germany
- Full name: Sven Schüle
Re: Simplifying code
Again. Focus on finding and fixing bugs. Not on language, not on speed. And I would not focus on simplification as well at this very moment, since you would probably replace old, complex, buggy code by new, less complex but still buggy code. Finding bugs and understanding why they happened will help a lot for the future, including to learn how to avoid them. And to learn how to find bugs ...
A bug can also be to use a wrong concept, of course. In that case you actually need to rewrite some code.
Simplification is a good preparation for making changes later on without breaking the code. It helps to understand correct code better, and to make it easier to maintain. But it does not necessarily help to see the bugs in the old code.
A bug can also be to use a wrong concept, of course. In that case you actually need to rewrite some code.
Simplification is a good preparation for making changes later on without breaking the code. It helps to understand correct code better, and to make it easier to maintain. But it does not necessarily help to see the bugs in the old code.
Sven Schüle (engine author: Jumbo, KnockOut, Surprise)
-
- Posts: 7220
- Joined: Mon May 27, 2013 10:31 am
Re: Simplifying code
I don't care about bugs for now. Bitboards is an implementation detail. A representation. So it should be hidden.
Bugs is result of missing test cases.
Don't know how to fix bad interpreted algorithm descriptions or bad algorithms, bad ideas or visions.
Bugs is result of missing test cases.
Don't know how to fix bad interpreted algorithm descriptions or bad algorithms, bad ideas or visions.
-
- Posts: 12542
- Joined: Wed Mar 08, 2006 8:57 pm
- Location: Redmond, WA USA
Re: Simplifying code
This is a mistake.
The great lesson of Fabian Letouzy is to write clear correct code and then build on that.
Building upon buggy code is a house of cards, and eventually the breeze will blow.
Taking ideas is not a vice, it is a virtue. We have another word for this. It is called learning.
But sharing ideas is an even greater virtue. We have another word for this. It is called teaching.
But sharing ideas is an even greater virtue. We have another word for this. It is called teaching.
-
- Posts: 7220
- Joined: Mon May 27, 2013 10:31 am
Re: Simplifying code
You forget I wrote: Bugs is result of missing test cases.
Writing clear code is first thing to do.
O wait that won't help if algorithms, ideas or visions are bad.
Writing clear code is first thing to do.
O wait that won't help if algorithms, ideas or visions are bad.
-
- Posts: 491
- Joined: Sat Mar 02, 2013 11:31 pm
Re: Simplifying code
Yes! Clear code is usually the best choice. Like Stroutstrup said:
Simple code is usually fast code.
Recently I removed KPK Bitbases from my engine. That clearly hurts ELO-wise. But simplifications ftw. I try to keep my engine pretty simple.
I recommend cloc to keep bloat under control.
Some examples:
Sapeli 1.88 (The whole project just 2250 lines):
Code: Select all
-------------------------------------------------------------------------------
Language files blank comment code
-------------------------------------------------------------------------------
C 1 273 105 2018
Python 1 34 6 124
Markdown 1 15 0 71
make 1 15 29 37
-------------------------------------------------------------------------------
SUM: 4 337 140 2250
-------------------------------------------------------------------------------
Others
lc0: 31788
Crafty: 27497
Rodent IV: 22526
Stockfish-dev: 8320
The worst offender BOOST has over 6.3M lines
Non chess:
Vue.js: 142187
BOOS: 6351090