Code Question

Discussion of chess software programming and technical issues.

Moderators: hgm, Rebel, chrisw

ChessRenewal
Posts: 15
Joined: Thu Jul 25, 2019 7:13 pm
Full name: Jay Warendorff

Code Question

Post by ChessRenewal »

Weiss 1.4 has (GenPawn in MoveGen.c and ParseTimeControl in uci.c respectively):

const Bitboard enemies = pos->checkers ?: colorBB(!color);

Limits.depth = Limits.depth ?: 100;

=============================================
In contrast Weiss 1.3 has

const Bitboard enemies = pos->checkers ? pos->checkers : colorBB(!color);

Limits.depth = !Limits.depth ? MAX_PLY : MIN(Limits.depth, MAX_PLY);
===============================================

Visual studio complains about the code above for 1.4 indicating it expects an expression.

Could someone please comment? Thanks.
Terje
Posts: 347
Joined: Tue Nov 19, 2019 4:34 am
Location: https://github.com/TerjeKir/weiss
Full name: Terje Kirstihagen

Re: Code Question

Post by Terje »

ChessRenewal wrote: Tue Jul 06, 2021 4:30 pm Weiss 1.4 has (GenPawn in MoveGen.c and ParseTimeControl in uci.c respectively):

const Bitboard enemies = pos->checkers ?: colorBB(!color);

Limits.depth = Limits.depth ?: 100;

=============================================
In contrast Weiss 1.3 has

const Bitboard enemies = pos->checkers ? pos->checkers : colorBB(!color);

Limits.depth = !Limits.depth ? MAX_PLY : MIN(Limits.depth, MAX_PLY);
===============================================

Visual studio complains about the code above for 1.4 indicating it expects an expression.

Could someone please comment? Thanks.
The Elvis operator is cool. A ?: B is equivalent to A ? A : B, however A is only evaluated once (when that matters). If MSVC doesn't support it you can replace it with the equivalent longer form (in these cases evaluating A twice is no different from once, so the generated code should be the same).
User avatar
mvanthoor
Posts: 1784
Joined: Wed Jul 03, 2019 4:42 pm
Location: Netherlands
Full name: Marcel Vanthoor

Re: Code Question

Post by mvanthoor »

Terje wrote: Tue Jul 06, 2021 4:48 pm The Elvis operator is cool. A ?: B is equivalent to A ? A : B...
OMG. A shorthand for a shorthand. I thought Rust was the only language doing stuff like that (with their generics, where they now have something like... 4 ways... to write them).

And those namse: Elvis, Walrus, Spaceship. Some time ago I sw a Youtube video about someone designing his own language which had a "Rocket" operator: >>=|>

The world's gone crazy or I'm getting old.
Author of Rustic, an engine written in Rust.
Releases | Code | Docs | Progress | CCRL
Terje
Posts: 347
Joined: Tue Nov 19, 2019 4:34 am
Location: https://github.com/TerjeKir/weiss
Full name: Terje Kirstihagen

Re: Code Question

Post by Terje »

mvanthoor wrote: Tue Jul 06, 2021 7:57 pm
Terje wrote: Tue Jul 06, 2021 4:48 pm The Elvis operator is cool. A ?: B is equivalent to A ? A : B...
OMG. A shorthand for a shorthand. I thought Rust was the only language doing stuff like that (with their generics, where they now have something like... 4 ways... to write them).

And those namse: Elvis, Walrus, Spaceship. Some time ago I sw a Youtube video about someone designing his own language which had a "Rocket" operator: >>=|>

The world's gone crazy or I'm getting old.
I need the rocket in C, whatever it does!
amanjpro
Posts: 883
Joined: Sat Mar 13, 2021 1:47 am
Full name: Amanj Sherwany

Re: Code Question

Post by amanjpro »

Oh I hate this... C++ is rivaling Scala in introducing "simple" and powerful operators
connor_mcmonigle
Posts: 530
Joined: Sun Sep 06, 2020 4:40 am
Full name: Connor McMonigle

Re: Code Question

Post by connor_mcmonigle »

amanjpro wrote: Wed Jul 07, 2021 2:31 am Oh I hate this... C++ is rivaling Scala in introducing "simple" and powerful operators
Weiss is written in C
amanjpro
Posts: 883
Joined: Sat Mar 13, 2021 1:47 am
Full name: Amanj Sherwany

Re: Code Question

Post by amanjpro »

connor_mcmonigle wrote: Wed Jul 07, 2021 4:09 am
amanjpro wrote: Wed Jul 07, 2021 2:31 am Oh I hate this... C++ is rivaling Scala in introducing "simple" and powerful operators
Weiss is written in C

Oops that hurt hhh
Terje
Posts: 347
Joined: Tue Nov 19, 2019 4:34 am
Location: https://github.com/TerjeKir/weiss
Full name: Terje Kirstihagen

Re: Code Question

Post by Terje »

Elvis is very useful for avoiding evaluating twice.

Code: Select all

int x = readInt() ? readInt() : 0;
The above obviously fails (reads twice), so you'd need to write something like:

Code: Select all

int inputInt = readInt();
int x = inputInt ? inputInt : 0;
Which is ugly compared to:

Code: Select all

int x = readInt() ?: 0;
Since the operator exists I use it when possible (even if no issues evaluating twice) as I find it neat :)
User avatar
mvanthoor
Posts: 1784
Joined: Wed Jul 03, 2019 4:42 pm
Location: Netherlands
Full name: Marcel Vanthoor

Re: Code Question

Post by mvanthoor »

Terje wrote: Thu Jul 08, 2021 5:43 pm Elvis is very useful for avoiding evaluating twice.

Code: Select all

int x = readInt() ? readInt() : 0;
The above obviously fails (reads twice), so you'd need to write something like:

Code: Select all

int inputInt = readInt();
int x = inputInt ? inputInt : 0;
Which is ugly compared to:

Code: Select all

int x = readInt() ?: 0;
Since the operator exists I use it when possible (even if no issues evaluating twice) as I find it neat :)
In Rust, I've shot myself in the foot several times because the compiler can optimize code and elide entire chunks all by itself. If you would write your first option, I'd not be surprised if the Rust compiler / LLVM realizes that it can cut out one evaluation by doing your second option, without you having to write it.

For example, I tried the "Replace modulo with key & (length - 1)" thing when "length" is a power of 2, but after extensive testing, it didn't improve anything. Then I discovered that you can replace modulo with "((key >> 32) * length) >> 32" for any number of TT indexes. Then I tried that, and it also didn't improve anything. I would not be surprised if either the Rust compiler or LLVM just "know" that "key % length" can be done as "((key >> 32) * nelem) >> 32", and you writing it yourself doesn't make a difference.

Rust highly prefers that you do the most logical thing. If you want to find something in a list, it prefers you to just loop over the entire list with an iterator and "break" or "return" when you find your value, instead of using a "while" loop and keeping an index and "found" condition manually.

I've found that optimizing some things like you would do in C, doesn't make a difference in Rust, or even worse, is counter-productive with regard to speed because you're effectively working against the compiler.
Author of Rustic, an engine written in Rust.
Releases | Code | Docs | Progress | CCRL
Terje
Posts: 347
Joined: Tue Nov 19, 2019 4:34 am
Location: https://github.com/TerjeKir/weiss
Full name: Terje Kirstihagen

Re: Code Question

Post by Terje »

mvanthoor wrote: Thu Jul 08, 2021 6:06 pm
Terje wrote: Thu Jul 08, 2021 5:43 pm Elvis is very useful for avoiding evaluating twice.

Code: Select all

int x = readInt() ? readInt() : 0;
The above obviously fails (reads twice), so you'd need to write something like:

Code: Select all

int inputInt = readInt();
int x = inputInt ? inputInt : 0;
Which is ugly compared to:

Code: Select all

int x = readInt() ?: 0;
Since the operator exists I use it when possible (even if no issues evaluating twice) as I find it neat :)
In Rust, I've shot myself in the foot several times because the compiler can optimize code and elide entire chunks all by itself. If you would write your first option, I'd not be surprised if the Rust compiler / LLVM realizes that it can cut out one evaluation by doing your second option, without you having to write it.
In the readInt() example above the compiler can't 'optimize' it, as not evaluating it twice would mean something different from the code I wrote, which says to evaluate it twice (and it matters in this case).

In the 2 examples from Weiss the compiler will give the same compiled code for either form as I mentioned in an earlier post here, as would it if I used a full declare -> assign in if/else. I use the elvis operator because the code looks better, and the intent is clearer (Elvis does exactly what I want, while full form ternary can do other things as well).
For example, I tried the "Replace modulo with key & (length - 1)" thing when "length" is a power of 2, but after extensive testing, it didn't improve anything. Then I discovered that you can replace modulo with "((key >> 32) * length) >> 32" for any number of TT indexes. Then I tried that, and it also didn't improve anything. I would not be surprised if either the Rust compiler or LLVM just "know" that "key % length" can be done as "((key >> 32) * nelem) >> 32", and you writing it yourself doesn't make a difference.
The compiler knows to optimize modulo by power of 2 into an &. The ((key >> 32) * length) >> 32 allows using non-power-of-2 sizes, it's slower than bitmasking (and thus modulo by power of 2) tho.
Rust highly prefers that you do the most logical thing. If you want to find something in a list, it prefers you to just loop over the entire list with an iterator and "break" or "return" when you find your value, instead of using a "while" loop and keeping an index and "found" condition manually.

I've found that optimizing some things like you would do in C, doesn't make a difference in Rust, or even worse, is counter-productive with regard to speed because you're effectively working against the compiler.
Many things you might think to optimize in C are similarly pointless as they are optimized by the compilers - they are incredibly good at optimizing code.

If you want to find something in a list in Rust, wouldn't Iterator::find be the way? :)