Page 1 of 1

About definitions in fruit

PostPosted: 02 Aug 2005, 09:55
by Uri Blass
I think that I understand the file colour.h of Fruit but I do not understand the reasons for the complex definitions in it.

#define COLOUR_IS_OK(colour) (((colour)&~1)==0)

I wonder what is the reason for that definition.

It seems to me simpler to write

#define COLOUR_IS_OK(colour)
(((colour)==0)||((colour)==1))

Later:

#define COLOUR_IS_WHITE(colour) ((colour)==White)
#define COLOUR_IS_BLACK(colour) ((colour)!=White)

I do not understand why colour!=White and not colour==Black in the second definition.

Later:

#define COLOUR_IS(piece,colour) (FLAG_IS((piece),COLOUR_FLAG(colour)))
#define FLAG_IS(piece,flag) (((piece)&(flag))!=0)

#define COLOUR_OPP(colour) ((colour)^(White^Black))
#define COLOUR_FLAG(colour) ((colour)+1)

Note that I never use definitions inside definitions in Movei and I did not know that it is correct code but
I do not understand the order of definitions and it seems that COLOUR_IS use definitions that are only defined later.

I also do not understand the reason for
#define COLOUR_OPP(colour) ((colour)^(White^Black))

Fruit has
const int White = 0;
const int Black = 1;
so I think that it is more simple to write
#define COLOUR_OPP(colour) ((colour)^1)

Also it is more clear to write
#define COLOUR_FLAG(colour) (1<<(colour))
and not
#define COLOUR_FLAG(colour) ((colour)+1)

These definitions are equal for colour=0 and colour=1 and I wonder what is the reason for prefering (colour)+1(speed optimization?)

I also understand that
COLOUR_IS(piece,colour) gives me 1 only if some piece has specific colour and piece is probably some information about some square that include the colour in 2 bits(piece&1 only if it is white when piece&2 only if it is black when neither of them if it is empty)

Uri

Re: About definitions in fruit

PostPosted: 02 Aug 2005, 10:19
by Alessandro Scotti
Hi Uri,
I think Fruit definitions are faster than what you suggest.
It's usually better to compare a value against zero rather than other values because processors keep an internal flag that is set if the last arithmetic operation returned zero, and always updated. So when you do "color & ~1", after the "and" operation the CPU already knows if the result was zero or not: the "== 0" part is free.
Similar reason for comparing against White (zero). Here the result is free if color was somehow resulting from an arithmetic expression, but even if it's not, comparing against zero is preferrable. For example on Intel CPUs testing if register EAX is zero could be implemented with "TEST EAX, EAX", which is faster than "CMP EAX, something". (At least, this used to be true some years ago, I don't know how that applies to newer processors).
I'm sure compilers know some other tricks as well, and they're pretty happy to find zero constants! :-)

Re: About definitions in fruit

PostPosted: 02 Aug 2005, 10:44
by Uri Blass
I wonder if compilers are so stupid not to translate both 1 and 2 to the same code when x is an integer or if there is a case when both expressions do not give the same value.

1)((x==0)||(x==1))
2)((x&~1)==0)

Uri

Re: About definitions in fruit

PostPosted: 02 Aug 2005, 10:54
by Alessandro Scotti
Uri Blass wrote:I wonder if compilers are so stupid not to translate both 1 and 2 to the same code when x is an integer

1)((x==0)||(x==1))
2)((x&~1)==0)
i


I just did the test using MSVC++ .NET 2003:

1)
test esi, esi
je ...
cmp esi, 1
jne ...

2)
test esi, -2 ; ~1
je ...

So case 2 saves a full compare, and the compiler even replaced "and" with "test", which is faster (it works like "and" but throws away the result, saving the write cycle).

Re: About definitions in fruit

PostPosted: 02 Aug 2005, 18:08
by Dann Corbit
Uri Blass wrote:I wonder if compilers are so stupid not to translate both 1 and 2 to the same code when x is an integer or if there is a case when both expressions do not give the same value.

1)((x==0)||(x==1))
2)((x&~1)==0)

Uri


This is the one that is not nearly as good:
1)((x==0)||(x==1))

An OR requires by the C and C++ standard a short circuit if the first test is true (the second condition is not even tested). That sounds good, but it implies a branch and therefore a possible missed branch prediction. The code of Fabien is clearly better in that case. A missed branch prediction is absurdly expensive.

A really good compiler might turn expression 1) into something better somehow, but the alternative is the better way to do it.

In any case, this test is not important and if you change it to the way you wrote it, it will not change the performance of Fruit even by 1%. If it is something that is executed millions of times, then it will become important. So that particular bit of code is the wrong place to focus.

On the other hand, if you get into the habit of writing branchless code when possible (and if the branchless code is obvious in what it does) then that is a good habit.