eval type

Discussion of chess software programming and technical issues.

Moderators: hgm, Rebel, chrisw

User avatar
lucasart
Posts: 3232
Joined: Mon May 31, 2010 1:29 pm
Full name: lucasart

eval type

Post by lucasart »

I'm wondering how to best choose the type of eval(opening, endgame). There are quite a few solutions:

1/ two ints (eg. Fruit)

Code: Select all

int op, eg;
Advantage: straight forward.
Disadvantage: No vectorized syntax. Lots of repetitive code to deal with op and eg at the same time.

2/ single int (eg. SF)

Code: Select all

int eval;
Advantage: vectorized syntax.
Disadvantage: still need to implement some operators manually (division doesn't work and needs to be done component by component for example). Also, it's a can of worms of portability issues and signed overflow problems, so it's easy to have subtle bugs with this.

3/ portable C++
Package two int in some structure and define operators. Either of:

Code: Select all

typedef int[2] eval_t;
typedef std&#58;&#58;array<int, 2> eval_t;
struct eval_t &#123; int op, eg; &#125;;
std&#58;&#58;pair<int, int>;
Advantage: vectorized syntax
Disadvantage: you need to define tons of operators (+move semantic).

4/ built-in vector syntax

Code: Select all

#ifdef __clang__
typedef int64_t eval_t __attribute__ (( ext_vector_type&#40;2&#41; ));
#else // assume GCC
typedef int64_t eval_t __attribute__ (( vector_size&#40;16&#41; ));
#endif
Advantage: Vectorized syntax. Performance. No need to define any operator manually.
Disadvantage: To get best performane (using SIMD), you need to make it 128 bit long instead of 64. That's twice more memory that you want (if you story many of those in memory, can increase cache pressure). Doesn't port beyond GCC and Clang.

Regarding 4/, I am wondering what happens with 64-bit only:

Code: Select all

#ifdef __clang__
typedef int eval_t __attribute__ (( ext_vector_type&#40;2&#41; ));
#else
typedef int eval_t __attribute__ (( vector_size&#40;8&#41; ));
#endif
Any chance the compiler can do something smart with that ? Or would it have to resort to software emulation equivalent to solution 3/ ?

What solution would did you choose for your engine ?
What would you choose, in hindsight ?
Theory and practice sometimes clash. And when that happens, theory loses. Every single time.
jdart
Posts: 4367
Joined: Fri Mar 10, 2006 5:23 am
Location: http://www.arasanchess.org

Re: eval type

Post by jdart »

My engine is C++ and I use a struct:

Code: Select all

    // The scores for opening, middlegame and endgame
    struct Scores &#123;
      Scores&#40;)
      &#58;mid&#40;0&#41;, end&#40;0&#41;, any&#40;0&#41;
      &#123;
      &#125;
      Scores&#40;const Scores &s&#41;
      &#58;mid&#40;s.mid&#41;,end&#40;s.end&#41;,any&#40;s.any&#41; &#123;
      &#125;
      int mid, end, any;
      int blend&#40;int materialLevel ) &#123;
        return any + mid*Params&#58;&#58;MATERIAL_SCALE&#91;materialLevel&#93;/128 +
          end*&#40;128-Params&#58;&#58;MATERIAL_SCALE&#91;materialLevel&#93;)/128;
      &#125;

      int operator == &#40;const Scores &s&#41; const &#123;
        return s.mid == mid && s.end == end && s.any == any;
      &#125;
      int operator != &#40;const Scores &s&#41; const &#123;
        return s.mid != mid || s.end != end || s.any != any;
      &#125;
    &#125;;
You could define other operators such as + and - if you want.

I don't think your choice of representation is going to have any significant impact on runtime speed, though.

--Jon
User avatar
lucasart
Posts: 3232
Joined: Mon May 31, 2010 1:29 pm
Full name: lucasart

Re: eval type

Post by lucasart »

Thank you. I hear you, it's probably not so performance sensitive anyway.

For now I'll go for solution (3), but keep the code flexible to be able to switch from (3) to (4), and back to (3), anytime I want, without having to rewrite any code.

Later down the road, I will do some performance measures and take it from there.

Code: Select all

/*#ifdef __clang__
typedef int eval_t __attribute__ (( ext_vector_type&#40;2&#41; ));
#else
typedef int eval_t __attribute__ (( vector_size&#40;8&#41; ));
#endif*/

typedef std&#58;&#58;array<int, 2> eval_t;

inline eval_t operator+&#40;eval_t e1, eval_t e2&#41; &#123; return &#123;e1&#91;0&#93; + e2&#91;0&#93;, e1&#91;1&#93; + e2&#91;1&#93;&#125;; &#125;
inline eval_t operator-&#40;eval_t e1, eval_t e2&#41; &#123; return &#123;e1&#91;0&#93; - e2&#91;0&#93;, e1&#91;1&#93; - e2&#91;1&#93;&#125;; &#125;

inline eval_t operator+=&#40;eval_t& e1, eval_t e2&#41; &#123; return e1&#91;0&#93; += e2&#91;0&#93;, e1&#91;1&#93; += e2&#91;1&#93;, e1; &#125;
inline eval_t operator-=&#40;eval_t& e1, eval_t e2&#41; &#123; return e1&#91;0&#93; -= e2&#91;0&#93;, e1&#91;1&#93; -= e2&#91;1&#93;, e1; &#125;

inline eval_t operator*&#40;eval_t e, int x&#41; &#123; return &#123;e&#91;0&#93; * x, e&#91;1&#93; * x&#125;; &#125;
inline eval_t operator/&#40;eval_t e, int x&#41; &#123; return &#123;e&#91;0&#93; / x, e&#91;1&#93; / x&#125;; &#125;

inline eval_t operator*=&#40;eval_t& e, int x&#41; &#123; return e&#91;0&#93; *= x, e&#91;1&#93; *= x, e; &#125;
inline eval_t operator/=&#40;eval_t& e, int x&#41; &#123; return e&#91;0&#93; /= x, e&#91;1&#93; /= x, e; &#125;

/* more operators as needed */
Theory and practice sometimes clash. And when that happens, theory loses. Every single time.
User avatar
cdani
Posts: 2204
Joined: Sat Jan 18, 2014 10:24 am
Location: Andorra

Re: eval type

Post by cdani »

For Andscacs first was 1/ and now is 2/. I did not test the change a lot, but the win in speed seemed to be minimal if any.
And sure, this 2nd provoked some more bugs.
User avatar
hgm
Posts: 27811
Joined: Fri Mar 10, 2006 10:06 am
Location: Amsterdam
Full name: H G Muller

Re: eval type

Post by hgm »

I prefer to use a single int, which holds opening-endgame + (endgame << 16). Another variable holds 1 + (gamePhase << 16), so that multiplying the two, and >> 16 give you the interpolated value. Note that gamePhase is a fractional number that runs from 1.0 to 0.0 during the game, and that because the integer part is stored in the high-order 16 bits, the low-order part is available for the fraction.
User avatar
lucasart
Posts: 3232
Joined: Mon May 31, 2010 1:29 pm
Full name: lucasart

Re: eval type

Post by lucasart »

hgm wrote:I prefer to use a single int, which holds opening-endgame + (endgame << 16). Another variable holds 1 + (gamePhase << 16), so that multiplying the two, and >> 16 give you the interpolated value. Note that gamePhase is a fractional number that runs from 1.0 to 0.0 during the game, and that because the integer part is stored in the high-order 16 bits, the low-order part is available for the fraction.
If everything is unsigned, that's fine. But once you start having signed opening and endgames scores, it's a real can of worms. It can work, of course (it does in SF), but it's tricky and easy to screw up.
Theory and practice sometimes clash. And when that happens, theory loses. Every single time.
Henk
Posts: 7220
Joined: Mon May 27, 2013 10:31 am

Re: eval type

Post by Henk »

I also use an int for eval and a double for gamePhase.
User avatar
hgm
Posts: 27811
Joined: Fri Mar 10, 2006 10:06 am
Location: Amsterdam
Full name: H G Muller

Re: eval type

Post by hgm »

lucasart wrote:If everything is unsigned, that's fine. But once you start having signed opening and endgames scores, it's a real can of worms. It can work, of course (it does in SF), but it's tricky and easy to screw up.
Everything is easy to screw up. As the low part contains a difference one would expect it to be signed. But the only effect of this is that the 1*(opening-endgame) will contibute a -1<<16 to the upper part when it is negative, which is basically a rounding error during the right-shift. I usually assume the evaluation is sufficiently fine-grained that this is without consequence.

If you are really worried about it, you could add 1<<15 before right-shifting, to round to closest instead of just clipping the fraction.

An alternative is to encode the numbers in excess notation, i.e. work with 0x8000 + eval instead of eval to make everything positive. Then you hav e to subtract 0x8000 afterwards. In either case it takes an extra addition/subtraction. Too bad these machines do not offer a shift+round instruction.
User avatar
lucasart
Posts: 3232
Joined: Mon May 31, 2010 1:29 pm
Full name: lucasart

Re: eval type

Post by lucasart »

just discovered something completely crazy:
http://stackoverflow.com/questions/2253 ... and-unions

Apparently, something like that:

Code: Select all

union &#123;
    struct &#123; int op, eg; &#125;;
    int v&#91;2&#93;;
&#125;;
Is illegal in C++, due to the unnamed struct. Even worse, it leads to undefined behaviour!

These standard gurus know their stuff, alright... But sometimes, the shit-rules they invent, just to break our code, it's beyond me :shock:

To comply with the C++ standard, and avoid this union/struct can of worms, I had to write a lot more cruft than expected:

Code: Select all

struct eval_t &#123;
	int v&#91;NB_PHASE&#93;;

	int operator&#91;&#93;&#40;int phase&#41; const &#123; return v&#91;phase&#93;; &#125;
	int op&#40;) const &#123; return v&#91;OPENING&#93;; &#125;
	int eg&#40;) const &#123; return v&#91;ENDGAME&#93;; &#125;

	int& operator&#91;&#93;&#40;int phase&#41; &#123; return v&#91;phase&#93;; &#125;
	int& op&#40;) &#123; return v&#91;OPENING&#93;; &#125;
	int& eg&#40;) &#123; return v&#91;ENDGAME&#93;; &#125;

	bool operator==&#40;eval_t e&#41; const &#123; return op&#40;) == e.op&#40;) && eg&#40;) == e.eg&#40;); &#125;
	bool operator!=&#40;eval_t e&#41; const &#123; return !(*this == e&#41;; &#125;

	eval_t operator+&#40;eval_t e&#41; const &#123; return &#123;op&#40;) + e.op&#40;), eg&#40;) + e.eg&#40;)&#125;; &#125;
	eval_t operator-&#40;eval_t e&#41; const &#123; return &#123;op&#40;) - e.op&#40;), eg&#40;) - e.eg&#40;)&#125;; &#125;
	eval_t operator*&#40;int x&#41; const &#123; return &#123;op&#40;) * x, eg&#40;) * x&#125;; &#125;
	eval_t operator/&#40;int x&#41; const &#123; return &#123;op&#40;) / x, eg&#40;) / x&#125;; &#125;

	eval_t operator+=&#40;eval_t e&#41; &#123; return op&#40;) += e.op&#40;), eg&#40;) += e.eg&#40;), *this; &#125;
	eval_t operator-=&#40;eval_t e&#41; &#123; return op&#40;) -= e.op&#40;), eg&#40;) -= e.eg&#40;), *this; &#125;
	eval_t operator*=&#40;int x&#41; &#123; return op&#40;) *= x, eg&#40;) *= x, *this; &#125;
	eval_t operator/=&#40;int x&#41; &#123; return op&#40;) /= x, eg&#40;) /= x, *this; &#125;
&#125;;
Last edited by lucasart on Sun Jun 21, 2015 10:44 am, edited 3 times in total.
Theory and practice sometimes clash. And when that happens, theory loses. Every single time.
elcabesa
Posts: 855
Joined: Sun May 23, 2010 1:32 pm

Re: eval type

Post by elcabesa »

i'm using this library, but it only works with sse simd.

http://www.agner.org/optimize/#vectorclass