visual studio – C# top level module takes up massive amounts of Self CPU


I recently noticed while testing my C# project on lower-spec hardware, that it ran extremely slow. For context, this is an FNA game utilizing its 3D drawing capabilities.

While checking in the debugger, I noticed that almost all of the CPU cycles were spent in the the top-level module itself.

The screenshot below is from my main machine where this only took up 20%:
A Visual Studio performance profiler call-tree window where the top-level module takes up 20% of Self CPU

What is happening in that Self CPU section? Where are those cycles going?
Is that a part of FNA’s 3D rendering? And if so, why is it displaying there in the performance analyzer, and not in the relevant methods?

EDIT To clarify “lower-spec”:
The reason I was concerned in the first place is that “lower-spec” here doesn’t mean an eepc, but rather a ~10 year old PC with an older NVIDIA Geforce 6XX series dedicated GPU.

EDIT 2:
Below is a test FNA Game with a similar amount of vertex buffers and vertices as my real project. There is no custom code aside from the code shown. The only project reference is the FNA.Core project.

using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;

namespace FNAPerformanceTest {
public class TestGame : Game {

    static void Main(string[] args) {
        using (TestGame g = new TestGame()) {
            g.Run();
        }
    }

    List<VertexBuffer> Buffers = new();

    GraphicsDeviceManager graphics;

    BasicEffect effect;

    public TestGame() {
        graphics = new GraphicsDeviceManager(this);
    }

    protected override void Initialize() {

        Random r = new Random();

        for(int a = 0; a < 200; ++a) {

            List<VertexPositionColor> vertices = new();

            for(int b = 0; b < 4096; ++b) {
                vertices.Add(new VertexPositionColor(new Vector3(r.NextSingle(), r.NextSingle(), r.NextSingle()), new Color(r.NextSingle(), r.NextSingle(), r.NextSingle())));
                vertices.Add(new VertexPositionColor(new Vector3(r.NextSingle(), r.NextSingle(), r.NextSingle()), new Color(r.NextSingle(), r.NextSingle(), r.NextSingle())));
                vertices.Add(new VertexPositionColor(new Vector3(r.NextSingle(), r.NextSingle(), r.NextSingle()), new Color(r.NextSingle(), r.NextSingle(), r.NextSingle())));
            }

            VertexBuffer buffer = new VertexBuffer(GraphicsDevice, typeof(VertexPositionColor), vertices.Count, BufferUsage.WriteOnly);
            buffer.SetData(vertices.ToArray());
            Buffers.Add(buffer);
        }

        effect = new BasicEffect(GraphicsDevice);

        base.Initialize();
    }

    protected override void Draw(GameTime gameTime) {

        foreach (var buffer in Buffers) {

            GraphicsDevice.SetVertexBuffer(buffer);

            foreach (EffectPass pass in effect.CurrentTechnique.Passes) {
                pass.Apply();
                GraphicsDevice.DrawPrimitives(PrimitiveType.TriangleList, 0, buffer.VertexCount / 3);
            }
        }

        base.Draw(gameTime);
    }
}
}

This is the performance profile this produces:
A Visual Studio performance profiler call-tree window where the top-level module takes up 45% of Self CPU

I would like to reiterate the Question of what it means to have Self CPU attributed to the top level here, rather than any specific method.

Leave a Reply

Your email address will not be published. Required fields are marked *