How can i improve my C# engine speed?
Moderator: Ras
-
spirch
- Posts: 95
- Joined: Fri Nov 09, 2012 12:36 am
Re: How can i improve my C# engine speed?
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?
It is inspired on how maksimKorzh's BBC engine dones the "communication" with the gui:Pio wrote: ↑Thu Jul 01, 2021 7:42 pmI 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.hgm wrote: ↑Thu Jul 01, 2021 10:28 am This is suspect:
Are you sure you don't mean 4095 here?Code: Select all
if ((searched_nodes & 4096) == 0 && AbortSearch)
I guess you would want to check if searched_nodes % 4096 == 0
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?
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.pedrojdm2021 wrote: ↑Thu Jul 01, 2021 8:10 pmIt is inspired on how maksimKorzh's BBC engine dones the "communication" with the gui:Pio wrote: ↑Thu Jul 01, 2021 7:42 pmI 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.hgm wrote: ↑Thu Jul 01, 2021 10:28 am This is suspect:
Are you sure you don't mean 4095 here?Code: Select all
if ((searched_nodes & 4096) == 0 && AbortSearch)
I guess you would want to check if searched_nodes % 4096 == 0
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?
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.Pio wrote: ↑Thu Jul 01, 2021 8:20 pmHe 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.pedrojdm2021 wrote: ↑Thu Jul 01, 2021 8:10 pmIt is inspired on how maksimKorzh's BBC engine dones the "communication" with the gui:Pio wrote: ↑Thu Jul 01, 2021 7:42 pmI 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.hgm wrote: ↑Thu Jul 01, 2021 10:28 am This is suspect:
Are you sure you don't mean 4095 here?Code: Select all
if ((searched_nodes & 4096) == 0 && AbortSearch)
I guess you would want to check if searched_nodes % 4096 == 0
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
-
pedrojdm2021
- Posts: 157
- Joined: Fri Apr 30, 2021 7:19 am
- Full name: Pedro Duran
Re: How can i improve my C# engine speed?
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?
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:Pio wrote: ↑Thu Jul 01, 2021 8:31 pmMake 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.Pio wrote: ↑Thu Jul 01, 2021 8:20 pmHe 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.pedrojdm2021 wrote: ↑Thu Jul 01, 2021 8:10 pmIt is inspired on how maksimKorzh's BBC engine dones the "communication" with the gui:Pio wrote: ↑Thu Jul 01, 2021 7:42 pmI 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.hgm wrote: ↑Thu Jul 01, 2021 10:28 am This is suspect:
Are you sure you don't mean 4095 here?Code: Select all
if ((searched_nodes & 4096) == 0 && AbortSearch)
I guess you would want to check if searched_nodes % 4096 == 0
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
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;
Code: Select all
bestmove e2a6 score -98 seached nodes 1286811
search done in 12523ms
-
pedrojdm2021
- Posts: 157
- Joined: Fri Apr 30, 2021 7:19 am
- Full name: Pedro Duran
Re: How can i improve my C# engine speed?
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 treespirch wrote: ↑Thu Jul 01, 2021 6:36 pm this, remove this listanytime you do aCode: Select all
public List<int> GenerateMoves() { moves = new List<int>(64);something, remove it or find a way to run it less oftenCode: Select all
= new
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![]()
this is a perft 5
https://i.imgur.com/GRyhtH7.png
![]()
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?
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.pedrojdm2021 wrote: ↑Thu Jul 01, 2021 8:54 pmThank 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 treespirch wrote: ↑Thu Jul 01, 2021 6:36 pm this, remove this listanytime you do aCode: Select all
public List<int> GenerateMoves() { moves = new List<int>(64);something, remove it or find a way to run it less oftenCode: Select all
= new
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![]()
this is a perft 5
https://i.imgur.com/GRyhtH7.png
![]()
another idea may be to switch to a data type that is more performant than a list?
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?
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 variablepedrojdm2021 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?
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?
ok i understand, but you mean just an array of movements for the board class, right?spirch wrote: ↑Thu Jul 01, 2021 9:06 pmmine is a fixed array initialized once with a counter, counter is at index 0 or at the end or it can be an external variablepedrojdm2021 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?
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
something like:
Code: Select all
int[] moves = null;
private void Init()
{
moves = new int[128];
}