How can i improve my C# engine speed?

Discussion of chess software programming and technical issues.

Moderator: Ras

spirch
Posts: 95
Joined: Fri Nov 09, 2012 12:36 am

Re: How can i improve my C# engine speed?

Post by spirch »

can you share your bit utility class? i have a weird feeling
pedrojdm2021
Posts: 157
Joined: Fri Apr 30, 2021 7:19 am
Full name: Pedro Duran

Re: How can i improve my C# engine speed?

Post by pedrojdm2021 »

Pio wrote: Thu Jul 01, 2021 7:42 pm
hgm wrote: Thu Jul 01, 2021 10:28 am This is suspect:

Code: Select all

            if ((searched_nodes & 4096) == 0 && AbortSearch)
Are you sure you don't mean 4095 here?
I agree with hgm. This looks really suspicious. It looks like you for the first 4096 nodes don’t look at AbortSearch, then for the next 4096 nodes you will look at AbortSearch every time and then not look for the next 4096, then look for next 4096…. . On average you will look on AbortSearch in 50 % of the cases. If AbortSearch is a property and does a lot that will be problematic.

I guess you would want to check if searched_nodes % 4096 == 0
It is inspired on how maksimKorzh's BBC engine dones the "communication" with the gui:
https://github.com/maksimKorzh/chess_pr ... bc.c#L3537

it does it in a very similar way to comunicate with the gui, but anyways i am going to disable that lines of code and check if there is difference in performance
Pio
Posts: 338
Joined: Sat Feb 25, 2012 10:42 pm
Location: Stockholm

Re: How can i improve my C# engine speed?

Post by Pio »

pedrojdm2021 wrote: Thu Jul 01, 2021 8:10 pm
Pio wrote: Thu Jul 01, 2021 7:42 pm
hgm wrote: Thu Jul 01, 2021 10:28 am This is suspect:

Code: Select all

            if ((searched_nodes & 4096) == 0 && AbortSearch)
Are you sure you don't mean 4095 here?
I agree with hgm. This looks really suspicious. It looks like you for the first 4096 nodes don’t look at AbortSearch, then for the next 4096 nodes you will look at AbortSearch every time and then not look for the next 4096, then look for next 4096…. . On average you will look on AbortSearch in 50 % of the cases. If AbortSearch is a property and does a lot that will be problematic.

I guess you would want to check if searched_nodes % 4096 == 0
It is inspired on how maksimKorzh's BBC engine dones the "communication" with the gui:
https://github.com/maksimKorzh/chess_pr ... bc.c#L3537

it does it in a very similar way to comunicate with the gui, but anyways i am going to disable that lines of code and check if there is difference in performance
He does what hgm says you should. You need to set the value to a power of two - 1, i.e. 2^x-1 to ensure all bits are set.
Pio
Posts: 338
Joined: Sat Feb 25, 2012 10:42 pm
Location: Stockholm

Re: How can i improve my C# engine speed?

Post by Pio »

Pio wrote: Thu Jul 01, 2021 8:20 pm
pedrojdm2021 wrote: Thu Jul 01, 2021 8:10 pm
Pio wrote: Thu Jul 01, 2021 7:42 pm
hgm wrote: Thu Jul 01, 2021 10:28 am This is suspect:

Code: Select all

            if ((searched_nodes & 4096) == 0 && AbortSearch)
Are you sure you don't mean 4095 here?
I agree with hgm. This looks really suspicious. It looks like you for the first 4096 nodes don’t look at AbortSearch, then for the next 4096 nodes you will look at AbortSearch every time and then not look for the next 4096, then look for next 4096…. . On average you will look on AbortSearch in 50 % of the cases. If AbortSearch is a property and does a lot that will be problematic.

I guess you would want to check if searched_nodes % 4096 == 0
It is inspired on how maksimKorzh's BBC engine dones the "communication" with the gui:
https://github.com/maksimKorzh/chess_pr ... bc.c#L3537

it does it in a very similar way to comunicate with the gui, but anyways i am going to disable that lines of code and check if there is difference in performance
He does what hgm says you should. You need to set the value to a power of two - 1, i.e. 2^x-1 to ensure all bits are set.
Make the change hgm suggested you to do. If you do something similar to what Maksim does but do it every second node you are in trouble. He does the the check every 2048 nodes while you do it every other node on average. That means you get a factor 1024 worse than him. This would explain the IO-problem since you read from the input 1024 times more often.
pedrojdm2021
Posts: 157
Joined: Fri Apr 30, 2021 7:19 am
Full name: Pedro Duran

Re: How can i improve my C# engine speed?

Post by pedrojdm2021 »

spirch wrote: Thu Jul 01, 2021 8:08 pm can you share your bit utility class? i have a weird feeling
Sure:

Code: Select all

 public static class BitUtility
    {
        private static ulong i = 0ul;

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static bool ContainsBit(ulong _bitboard, int _index) 
        {
            return (1ul & (_bitboard >> _index)) > 0;
        }

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static void SetBit(ref ulong _bitboard, int _index)
        {
            _bitboard |= (1ul << _index);
        }

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static void PopBit(ref ulong _bitboard, int _square)
        {
            _bitboard &= ~(1ul << _square);
        }

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static byte CountBits(ulong _bitboard)
        {
            // Hacky trick to count number of bits, it is 2x faster than the older version
            i = _bitboard;
            i = i - ((i >> 1) & 0x5555555555555555UL);
            i = (i & 0x3333333333333333UL) + ((i >> 2) & 0x3333333333333333UL);
            return (byte)(unchecked(((i + (i >> 4)) & 0xF0F0F0F0F0F0F0FUL) * 0x101010101010101UL) >> 56);
        }

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static byte GetLS1BIndex(ulong _bitboard)
        {
            return CountBits((_bitboard & (0 - _bitboard)) - 1);
        }

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static void RemoveLS1B(ref ulong _bitboard)
        {
            _bitboard &= _bitboard - 1;
        }
    }
pedrojdm2021
Posts: 157
Joined: Fri Apr 30, 2021 7:19 am
Full name: Pedro Duran

Re: How can i improve my C# engine speed?

Post by pedrojdm2021 »

Pio wrote: Thu Jul 01, 2021 8:31 pm
Pio wrote: Thu Jul 01, 2021 8:20 pm
pedrojdm2021 wrote: Thu Jul 01, 2021 8:10 pm
Pio wrote: Thu Jul 01, 2021 7:42 pm
hgm wrote: Thu Jul 01, 2021 10:28 am This is suspect:

Code: Select all

            if ((searched_nodes & 4096) == 0 && AbortSearch)
Are you sure you don't mean 4095 here?
I agree with hgm. This looks really suspicious. It looks like you for the first 4096 nodes don’t look at AbortSearch, then for the next 4096 nodes you will look at AbortSearch every time and then not look for the next 4096, then look for next 4096…. . On average you will look on AbortSearch in 50 % of the cases. If AbortSearch is a property and does a lot that will be problematic.

I guess you would want to check if searched_nodes % 4096 == 0
It is inspired on how maksimKorzh's BBC engine dones the "communication" with the gui:
https://github.com/maksimKorzh/chess_pr ... bc.c#L3537

it does it in a very similar way to comunicate with the gui, but anyways i am going to disable that lines of code and check if there is difference in performance
He does what hgm says you should. You need to set the value to a power of two - 1, i.e. 2^x-1 to ensure all bits are set.
Make the change hgm suggested you to do. If you do something similar to what Maksim does but do it every second node you are in trouble. He does the the check every 2048 nodes while you do it every other node on average. That means you get a factor 1024 worse than him. This would explain the IO-problem since you read from the input 1024 times more often.
hmmm i don't think that it will be the root of the problem, for example in each node you try to get a tranposition table struct from the current board's hash, it is usually heavier than a simple if. Look:

Code: Select all

  // ------------------[ Main AI Driver ]------------------------

        // Fail hard alpha-beta negamax implementation
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        private int Negamax(int _alpha, int _beta, int _depth, bool _makeNull = true)
        {
            // // check if the search is cancelled every 4096 nodes
            // if ((searched_nodes & 4096) == 0 && AbortSearch)
            // {
            //     return 0;
            // }

            // -------- [ Transposition table lookup ] ----------------------------

            TTEntry entry = TTable.Lookup(board.hash);
            if (entry.hash == board.hash && entry.depth >= _depth)
            {
                if (entry.eval < -MATE_SCORE) entry.eval += ply;
                if (entry.eval > MATE_SCORE) entry.eval -= ply;

                if (entry.flag == TTFlag.EXACT) 
                {
                    return entry.eval;
                }
                else if (entry.flag == TTFlag.ALPHA && entry.eval <= _alpha) 
                    return _alpha;
                else if (entry.flag == TTFlag.BETA && entry.eval >= _beta)
                    return _beta;
            }
            TTFlag flag = TTFlag.ALPHA;
            
i disabled the code and, it gave this results:

Code: Select all

bestmove e2a6 score -98 seached nodes 1286811
search done in 12523ms 
is is the same as before when i added the mailbox array to the lookup of the pieces :/
pedrojdm2021
Posts: 157
Joined: Fri Apr 30, 2021 7:19 am
Full name: Pedro Duran

Re: How can i improve my C# engine speed?

Post by pedrojdm2021 »

spirch wrote: Thu Jul 01, 2021 6:36 pm this, remove this list

Code: Select all

public List<int> GenerateMoves()
        {
            moves = new List<int>(64);
anytime you do a

Code: Select all

= new
something, remove it or find a way to run it less often

suggestion, go get the 5 day trial of dotmemory, see the flat line? this should be your goal, you want the memory to be dead, not alive :twisted:

this is a perft 5
https://i.imgur.com/GRyhtH7.png

Image
Thank you very much for your orientation, but the problem is... how? i have tried that by just cleaning the list, but then the recursion logic gets broken: each time you do a .Clear() all references from that list gets clearned too! so if on depth 4 i do a .Clear when called generating moves, all the parent nodes gets clearned, too! that results in a broken tree

another idea may be to switch to a data type that is more performant than a list?
Pio
Posts: 338
Joined: Sat Feb 25, 2012 10:42 pm
Location: Stockholm

Re: How can i improve my C# engine speed?

Post by Pio »

pedrojdm2021 wrote: Thu Jul 01, 2021 8:54 pm
spirch wrote: Thu Jul 01, 2021 6:36 pm this, remove this list

Code: Select all

public List<int> GenerateMoves()
        {
            moves = new List<int>(64);
anytime you do a

Code: Select all

= new
something, remove it or find a way to run it less often

suggestion, go get the 5 day trial of dotmemory, see the flat line? this should be your goal, you want the memory to be dead, not alive :twisted:

this is a perft 5
https://i.imgur.com/GRyhtH7.png

Image
Thank you very much for your orientation, but the problem is... how? i have tried that by just cleaning the list, but then the recursion logic gets broken: each time you do a .Clear() all references from that list gets clearned too! so if on depth 4 i do a .Clear when called generating moves, all the parent nodes gets clearned, too! that results in a broken tree

another idea may be to switch to a data type that is more performant than a list?
You can just create an array of size 128 (should be more than enough. Usually when you have fixed size arrays it is nice to set a dummy element as the last element and once reaching the dummy element you know you have processed everything.

Also make sure you run your code under release configuration and not debug configuration while testing performance.
spirch
Posts: 95
Joined: Fri Nov 09, 2012 12:36 am

Re: How can i improve my C# engine speed?

Post by spirch »

pedrojdm2021 wrote: Thu Jul 01, 2021 8:54 pm Thank you very much for your orientation, but the problem is... how? i have tried that by just cleaning the list, but then the recursion logic gets broken: each time you do a .Clear() all references from that list gets clearned too! so if on depth 4 i do a .Clear when called generating moves, all the parent nodes gets clearned, too! that results in a broken tree

another idea may be to switch to a data type that is more performant than a list?
mine is a fixed array initialized once with a counter, counter is at index 0 or at the end or it can be an external variable

increase the counter when you add, decrease the counter when you remove, set the counter to 0 (or 1 or -1 or what you feel is right) to reset the array, no need to clear or reset anything set

use the that counter to loop from n to counter
pedrojdm2021
Posts: 157
Joined: Fri Apr 30, 2021 7:19 am
Full name: Pedro Duran

Re: How can i improve my C# engine speed?

Post by pedrojdm2021 »

spirch wrote: Thu Jul 01, 2021 9:06 pm
pedrojdm2021 wrote: Thu Jul 01, 2021 8:54 pm Thank you very much for your orientation, but the problem is... how? i have tried that by just cleaning the list, but then the recursion logic gets broken: each time you do a .Clear() all references from that list gets clearned too! so if on depth 4 i do a .Clear when called generating moves, all the parent nodes gets clearned, too! that results in a broken tree

another idea may be to switch to a data type that is more performant than a list?
mine is a fixed array initialized once with a counter, counter is at index 0 or at the end or it can be an external variable

increase the counter when you add, decrease the counter when you remove, set the counter to 0 (or 1 or -1 or what you feel is right) to reset the array

use the that counter to loop from n to counter
ok i understand, but you mean just an array of movements for the board class, right?

something like:

Code: Select all

int[] moves = null;

private void Init()
{
	moves = new int[128];
}