C# Performance

Discussion of chess software programming and technical issues.

Moderators: hgm, Rebel, chrisw

Richard Allbert
Posts: 792
Joined: Wed Jul 19, 2006 9:58 am

Re: C# Performance

Post by Richard Allbert »

No, just what's posted here (which is nearly all of it). Although if it's easier, I could make it available for download.

It's not a working program... (yet)

Richard
RoadWarrior
Posts: 73
Joined: Fri Jan 13, 2012 12:39 am
Location: London, England

Re: C# Performance

Post by RoadWarrior »

Richard Allbert wrote:Results were:

1881 knps
1726 knps
1791 knps
1818 knps

1804 knps avg
In the latest version of your code, which are the expensive "parent" methods as highlighted by your performance profiler? By "parent" methods, I mean methods that have the longest runtime excluding any time taken to call their child methods.

It's usually better to identify the expensive methods first, rather than focusing on expensive source code lines, as most intrusive profilers struggle with accurate numbers when asked to look at all source code lines. But once you've identified the expensive methods, then you can focus on profiling individual lines within those methods.
There are two types of people in the world: Avoid them both.
Richard Allbert
Posts: 792
Joined: Wed Jul 19, 2006 9:58 am

Re: C# Performance

Post by Richard Allbert »

I thought it was sqAttacked :).

Anyway, a new profile today using the MS CLR4 Profiler.

I ran the position to Depth 4, because the profiler slowed things down a lot.

Following the MS steps, first is to look at Heap allocated bytes.
-----------------------
Top was EngineMove.Move with 489MB of allocations, next was List<T> with 2MB.

The rest are all small (under 40kB).

From what I'm reading online in the examples, 489MB is a lot.
--------------------------

Reallocated bytes:

Top is EngineMove.Move with 2.6MB, the rest are tiny

------------------------------

Final Heap bytes:

Top again EngineMove.Move with 400kB, rest are all small

------------------------------

GC collections: 490, the profiler failed to build the data. The MS example has 30.

I'm waiting for the function call tree to build, which is taking ages, and unfortunately I have to go and pick someone up from their work, so I'll not be able to post the details until later.

Regards

Richard
Richard Allbert
Posts: 792
Joined: Wed Jul 19, 2006 9:58 am

Re: C# Performance

Post by Richard Allbert »

Forgot to say, it ran for about a minute
RoadWarrior
Posts: 73
Joined: Fri Jan 13, 2012 12:39 am
Location: London, England

Re: C# Performance

Post by RoadWarrior »

I was actually asking for the performance numbers rather than the memory numbers.
Richard Allbert wrote:Following the MS steps, first is to look at Heap allocated bytes.
-----------------------
Top was EngineMove.Move with 489MB of allocations, next was List<T> with 2MB.

The rest are all small (under 40kB).

From what I'm reading online in the examples, 489MB is a lot.
I thought your Move was a struct, which would normally be allocated on the stack rather than the heap. If it is a struct, is it boxed or a field in a class? If so, it will be heap-allocated, which could explain a lot of your performance issues.
There are two types of people in the world: Avoid them both.
Richard Allbert
Posts: 792
Joined: Wed Jul 19, 2006 9:58 am

Re: C# Performance

Post by Richard Allbert »

Yes, it is a struct.

Now for the profiling info...

I used the normal position, but to depth 4 twice, second mirrored with Black to Move, so same nodes each time

Here are the two tables, produced by the ANTS Profiler:

Image

And

Image

Now I'm using a decent profiler, hopefully things will be a bit clearer!

I appreciate the help

Richard

PS I will look at the move structure now
PPS I see you can even see the timings by line of source code
Richard Allbert
Posts: 792
Joined: Wed Jul 19, 2006 9:58 am

Re: C# Performance

Post by Richard Allbert »

Move as a class improved things to 2800 knps !

Here is the new profile... % are pretty much the same

Image

Image

Richard
RoadWarrior
Posts: 73
Joined: Fri Jan 13, 2012 12:39 am
Location: London, England

Re: C# Performance

Post by RoadWarrior »

Richard Allbert wrote:Move as a class improved things to 2800 knps !
Are you saying that Move is defined as a class, or as a struct which is a member of a class?

Both of these will be heap-allocated, which is slower. Defining Move as a struct on its own means it will be stack-allocated, which is faster. That may be what you've done, which would indeed explain the impressive speed-up.

I will look at your Ants profiler numbers later tonight.
There are two types of people in the world: Avoid them both.
Richard Allbert
Posts: 792
Joined: Wed Jul 19, 2006 9:58 am

Re: C# Performance

Post by Richard Allbert »

Move was originally defined on its own as a struct, the results between

1950 - 2050 knps

Code: Select all

public struct Move
    &#123;
        public int from;
        public int to;
        public int promotedTo;
        public int captured;
        public int flag;

        public Move&#40;int f, int t, int pr, int ca, int fl&#41;
        &#123;
            from = f;
            to = t;
            promotedTo = pr;
            captured = ca;
            flag = fl;
        &#125;
    &#125;
and changed to the following, giving 2650 - 2800 knps

Code: Select all

public class Move
    &#123;
        public int from;
        public int to;
        public int promotedTo;
        public int captured;
        public int flag;

        public Move&#40;int f, int t, int pr, int ca, int fl&#41;
        &#123;
            from = f;
            to = t;
            promotedTo = pr;
            captured = ca;
            flag = fl;
        &#125;
    &#125;
In the perft function, a list reference is sent into the move generator

Code: Select all

List<Move> list = new List<Move>&#40;Moves.MaxMoveListMoves&#41;;
MoveGenerator.GenerateMoves&#40;tree.board, list&#41;;
I ran it a few times with each setting, as there is quite a bit of variability.
RoadWarrior
Posts: 73
Joined: Fri Jan 13, 2012 12:39 am
Location: London, England

Re: C# Performance

Post by RoadWarrior »

Richard Allbert wrote:Move was originally defined on its own as a struct, the results between

1950 - 2050 knps

Code: Select all

public struct Move
    &#123;
        public int from;
        public int to;
        public int promotedTo;
        public int captured;
        public int flag;

        public Move&#40;int f, int t, int pr, int ca, int fl&#41;
        &#123;
            from = f;
            to = t;
            promotedTo = pr;
            captured = ca;
            flag = fl;
        &#125;
    &#125;
and changed to the following, giving 2650 - 2800 knps

Code: Select all

public class Move
    &#123;
        public int from;
        public int to;
        public int promotedTo;
        public int captured;
        public int flag;

        public Move&#40;int f, int t, int pr, int ca, int fl&#41;
        &#123;
            from = f;
            to = t;
            promotedTo = pr;
            captured = ca;
            flag = fl;
        &#125;
    &#125;
Okay, as a first-order effect, that makes little sense to me. A 50% speed-up just by migrating from a stack-allocated struct to a heap-allocated class is definitely not what I would expect.

Perhaps this is some sort of second-order effect, where there was a very in-efficient use of the struct in some way, such as the struct being boxed. Or perhaps my intuition is just wrong in this instance. :D

If you were using a struct defined by itself (EngineMove.Move), was EngineMove just a namespace and not a class? It's very odd that the CLR profiler was showing the struct as heap-allocated. That normally only happens when a struct is defined as a member of a class or if it's boxed in some way.

If your struct was boxed, then migrating to a class would definitely increase the speed. If you found and removed the boxing, then migrating back to a struct should give you another significant speed-up. :idea:
There are two types of people in the world: Avoid them both.