【问题标题】:C# Multithreading - Performance and architecture of systemC# 多线程 - 系统的性能和体系结构
【发布时间】:2014-06-11 04:18:00
【问题描述】:

所以我正在开发一个 C# 应用程序,它非常占用 CPU。

目前我正在使用 ThreadPool 异步处理任务,但事实证明这并没有像我预期的那样工作。

拿这个类我用它来检索一个Builder类来生成一个块。

public class ChunkBuilderProvider
{
    private readonly BlockingCollection<ChunkBuilder> Builders;

    public ChunkBuilderProvider()
    {
        Builders = new BlockingCollection<ChunkBuilder>();
        for (int i = 0; i < Configs.BuilderMaxInstance; i++)
            Builders.Add(new ChunkBuilder());
    }

    public ChunkBuilder GetBuilder()
    {
        ChunkBuilder c;
        return Builders.TryTake(out c, -1) ? c : null;
    }

    public void ReplaceBuilder(ChunkBuilder c)
    {
        Builders.Add(c);
    }

    public int IdleBuilders()
    {
        return Builders.Count;
    }

    internal bool Build(Chunk c)
    {
        if (c.State == Chunk.ChunkState.Generating)
            return false;
        var b = GetBuilder();
        if (b == null)
            return false;
        ThreadPool.QueueUserWorkItem(a =>
        {
            b.Generate(c);
            ReplaceBuilder(b);
        });
        return true;
    }
}

Generate 任务占用大量 CPU 资源,使用 5 个构建器运行此任务,将我的 CPU 使用率提高到 100%。

蚂蚁给我看这个:

[这里好像不能发图]

编辑:CPU 密集型代码是这样的:

    using System;
using System.Collections.Generic;
using HyroVoxelEngine.Graphics.Primitives;
using HyroVoxelEngine.Voxels.Blocks;
using HyroVoxelEngine.Voxels.Chunks;
using SharpDX;


namespace HyroVoxelEngine.Voxels.Meshing
{
    public class GreedyMeshing
    {
        private static readonly int[][][] VerticesOffset = new int[6][][]
        {
//TOP
            new int[9][]
            {
                new int[3] {-1, 1, 1}, new int[3] {0, 1, 1}, new int[3] {1, 1, 1}, new int[3] {-1, 1, 0}, new int[3] {0, 1, 0}, new int[3] {1, 1, 0}, new int[3] {-1, 1, -1},
                new int[3] {0, 1, -1}, new int[3] {1, 1, -1}
            },
//North
            new int[9][]
            {
                new int[3] {-1, -1, 1}, new int[3] {0, -1, 1}, new int[3] {1, -1, 1}, new int[3] {-1, 0, 1}, new int[3] {0, 0, 1}, new int[3] {1, 0, 1}, new int[3] {-1, 1, 1},
                new int[3] {0, 1, 1}, new int[3] {1, 1, 1}
            },
//Bottom
            new int[9][]
            {
                new int[3] {-1, -1, -1}, new int[3] {0, -1, -1}, new int[3] {1, -1, -1}, new int[3] {-1, -1, 0}, new int[3] {0, -1, 0}, new int[3] {1, -1, 0}, new int[3] {-1, -1, 1},
                new int[3] {0, -1, 1}, new int[3] {1, -1, 1}
            },
//SOUTH
            new int[9][]
            {
                new int[3] {-1, 1, -1}, new int[3] {0, 1, -1}, new int[3] {1, 1, -1}, new int[3] {-1, 0, -1}, new int[3] {0, 0, -1}, new int[3] {1, 0, -1}, new int[3] {-1, -1, -1},
                new int[3] {0, -1, -1}, new int[3] {1, -1, -1}
            },
//West
            new int[9][]
            {
                new int[3] {1, 1, 1}, new int[3] {1, 0, 1}, new int[3] {1, -1, 1}, new int[3] {1, 1, 0}, new int[3] {1, 0, 0}, new int[3] {1, -1, 0}, new int[3] {1, 1, -1},
                new int[3] {1, 0, -1}, new int[3] {1, -1, -1}
            },
//East
            new int[9][]
            {
                new int[3] {-1, -1, 1}, new int[3] {-1, 0, 1}, new int[3] {-1, 1, 1}, new int[3] {-1, -1, 0}, new int[3] {-1, 0, 0}, new int[3] {-1, 1, 0}, new int[3] {-1, -1, -1},
                new int[3] {-1, 0, -1}, new int[3] {-1, 1, -1}
            }
        };

        private Block[][][] Blocks;
        private List<int> Index;
        private int VOXEL_SIZE = 1;
        private Chunk chunk;
        private List<VoxelVertex> vertices;
        public void SetChunk(Chunk c)
        {
            chunk = c;
            Blocks = c.Blocks;
        }
        public ChunkPrimitive Greedy()
        {
            Index = new List<int>(10000);
            vertices = new List<VoxelVertex>(8000);
            /*
         * These are just working variables for the algorithm - almost all taken 
         * directly from Mikola Lysenko's javascript implementation.
         */
            int i, j, k, l, w, h, u, v, n;
            var side = VoxelFace.Direction.None;
            int[] x = {0, 0, 0};
            int[] q = {0, 0, 0};
            int[] du = {0, 0, 0};
            int[] dv = {0, 0, 0};
            /*
         * We create a mask - this will contain the groups of matching voxel faces 
         * as we proceed through the chunk in 6 directions - once for each face.
         */
            VoxelFace voxelFace, voxelFace1;
            int[] Dimensions = {Chunk.SizeX, Chunk.SizeY, Chunk.SizeZ};
            VoxelFace[] mask;
            /**
         * We start with the lesser-spotted boolean for-loop (also known as the old flippy floppy). 
         * 
         * The variable backFace will be TRUE on the first iteration and FALSE on the second - this allows 
         * us to track which direction the indices should run during creation of the quad.
         * 
         * This loop runs twice, and the inner loop 3 times - totally 6 iterations - one for each 
         * voxel face.
         */
            for (bool backFace = true, b = false; b != backFace; backFace = backFace && b, b = !b)
            {
                /*
             * We sweep over the 3 dimensions - most of what follows is well described by Mikola Lysenko 
             * in his post - and is ported from his Javascript implementation.  Where this implementation 
             * diverges, I've added commentary.
             */
                for (int d = 0; d < 3; d++)
                {
                    /*
                 * These are just working variables to hold two faces during comparison.
                 */
                    u = (d + 1)%3;
                    v = (d + 2)%3;
                    x[0] = 0;
                    x[1] = 0;
                    x[2] = 0;
                    q[0] = 0;
                    q[1] = 0;
                    q[2] = 0;
                    q[d] = 1;
                    mask = new VoxelFace[Dimensions[u]*Dimensions[v]];
                    /*
                 * Here we're keeping track of the side that we're meshing.
                 */
                    if (d == 0)
                        side = backFace ? VoxelFace.Direction.West : VoxelFace.Direction.East;
                    else if (d == 1)
                        side = backFace ? VoxelFace.Direction.Bottom : VoxelFace.Direction.Top;
                    else if (d == 2)
                        side = backFace ? VoxelFace.Direction.South : VoxelFace.Direction.North;
                    /*
                 * We move through the dimension from front to back
                 */
                    for (x[d] = -1; x[d] < Dimensions[d];)
                    {
                        n = 0;
                        for (x[v] = 0; x[v] < Dimensions[v]; x[v]++)
                        {
                            for (x[u] = 0; x[u] < Dimensions[u]; x[u]++)
                            {
                                /*
                             * Here we retrieve two voxel faces for comparison.
                             */
                                voxelFace = (x[d] >= 0) ? getVoxelFace(x[0], x[1], x[2], side) : null;
                                voxelFace1 = (x[d] < Dimensions[d] - 1) ? getVoxelFace(x[0] + q[0], x[1] + q[1], x[2] + q[2], side) : null;
                                mask[n++] = ((voxelFace != null && voxelFace1 != null && voxelFace.Equals(voxelFace1))) ? null : backFace ? voxelFace1 : voxelFace;
                            }
                        }
                        x[d]++;
                        /*
                     * Now we generate the mesh for the mask
                     */
                        n = 0;
                        for (j = 0; j < Dimensions[v]; j++)
                        {
                            for (i = 0; i < Dimensions[u];)
                            {
                                if (mask[n] != null)
                                {
                                    /*
                                 * We compute the width
                                 */
                                    for (w = 1; i + w < Dimensions[u] && mask[n + w] != null && mask[n + w].Equals(mask[n]); w++) {}
                                    /*
                                 * Then we compute height
                                 */
                                    bool done = false;
                                    for (h = 1; j + h < Dimensions[v]; h++)
                                    {
                                        for (k = 0; k < w; k++)
                                        {
                                            if (mask[n + k + h*Dimensions[u]] == null || !mask[n + k + h*Dimensions[u]].Equals(mask[n]))
                                            {
                                                done = true;
                                                break;
                                            }
                                        }
                                        if (done)
                                            break;
                                    }
                                    /*
                                 * Here we check the "transparent" attribute in the VoxelFace class to ensure that we don't mesh 
                                 * any culled faces.
                                 */
                                    if (!mask[n].Transparent)
                                    {
                                        /*
                                     * Add quad
                                     */
                                        x[u] = i;
                                        x[v] = j;
                                        du[0] = 0;
                                        du[1] = 0;
                                        du[2] = 0;
                                        du[u] = w;
                                        dv[0] = 0;
                                        dv[1] = 0;
                                        dv[2] = 0;
                                        dv[v] = h;
                                        /*
                                     * And here we call the quad function in order to render a merged quad in the scene.
                                     * 
                                     * We pass mask[n] to the function, which is an instance of the VoxelFace class containing 
                                     * all the attributes of the face - which allows for variables to be passed to shaders - for 
                                     * example lighting values used to create ambient occlusion.
                                     */
                                        Quad(new Vector3(x[0], x[1], x[2]), new Vector3(x[0] + du[0], x[1] + du[1], x[2] + du[2]),
                                            new Vector3(x[0] + du[0] + dv[0], x[1] + du[1] + dv[1], x[2] + du[2] + dv[2]), new Vector3(x[0] + dv[0], x[1] + dv[1], x[2] + dv[2]), w, h,
                                            mask[n], backFace);
                                    }
                                    /*
                                 * We zero out the mask
                                 */
                                    for (l = 0; l < h; ++l)
                                    {
                                        for (k = 0; k < w; ++k)
                                            mask[n + k + l*Dimensions[u]] = null;
                                    }
                                    /*
                                 * And then finally increment the counters and continue
                                 */
                                    i += w;
                                    n += w;
                                }
                                else
                                {
                                    i++;
                                    n++;
                                }
                            }
                        }
                    }
                }
            }
            if (vertices.Count == 0 || Index.Count == 0)
                return null;
            return new ChunkPrimitive(vertices.ToArray(), Index.ToArray());
        }
        private VoxelFace getVoxelFace(int x, int y, int z, VoxelFace.Direction side)
        {
            VoxelFace voxelFace = new VoxelFace(side);
            voxelFace.Type = Blocks[x][y][z].Type;
            voxelFace.Light = chunk.LightValue[x][y][z];
            voxelFace.Side = side;
            voxelFace.LightSettings = CountSolidCorner(voxelFace, x, y, z);
            return voxelFace;
        }

        private int[] CountSolidCorner(VoxelFace voxelFace, int x, int y, int z)
        {
            var side = voxelFace.Side;
            int bottomLeft = 0;
            int bottomRight = 0;
            int TopLeft = 0;
            int TopRight = 0;
            var pos = new Vector3(x, y, z);

            #region TOP BOTOM

            //SOUTH = -z
            //NORTH = +z
            //West = -X
            //est = X;
            int[][] vertOff = VerticesOffset[(int) side];
            if (GetBlockSolid(vertOff[6], x, y, z))
                bottomLeft++;
            if (GetBlockSolid(vertOff[8], x, y, z))
                bottomRight++;
            if (GetBlockSolid(vertOff[2], x, y, z))
                TopRight++;
            if (GetBlockSolid(vertOff[0], x, y, z))
                TopLeft++;
            if (GetBlockSolid(vertOff[1], x, y, z))
            {
                TopLeft++;
                TopRight++;
            }
            if (GetBlockSolid(vertOff[7], x, y, z))
            {
                bottomLeft++;
                bottomRight++;
            }
            if (GetBlockSolid(vertOff[3], x, y, z))
            {
                TopLeft++;
                bottomLeft++;
            }
            if (GetBlockSolid(vertOff[5], x, y, z))
            {
                TopRight++;
                bottomRight++;
            }

            if (side == VoxelFace.Direction.Bottom)
                return new[] {TopLeft, TopRight, bottomLeft, bottomRight};
            if (side == VoxelFace.Direction.Top)
                return new[] {bottomLeft, bottomRight, TopLeft, TopRight};
            if (side == VoxelFace.Direction.West)
                return new[] {bottomLeft, TopLeft, bottomRight, TopRight};
            if (side == VoxelFace.Direction.East)
                return new[] {bottomRight, TopRight, bottomLeft, TopLeft};
            if (side == VoxelFace.Direction.North)
                return new[] {TopLeft, bottomLeft, TopRight, bottomRight};

            #endregion

            //COM x Positivo
            //TOP -  TR - BR - TL - BL
            return new[] {4, 4, 4, 4};
        }

        private bool GetBlockSolid(int[] offset, int x, int y, int z)
        {
            x = x + offset[0];
            y = y + offset[1];
            z = z + offset[2];
            if (x  < 0 | y  < 0 | z < 0)
                return true;
            if (x  >= Chunk.SizeX | y  >= Chunk.SizeY | z  >= Chunk.SizeZ)
                return true;
            return !Block.IsSolidBlock(Blocks[x][y ][z ].Type);
        }
        private void Quad(Vector3 bottomLeft, Vector3 topLeft, Vector3 topRight, Vector3 bottomRight, int width, int height, VoxelFace voxel, bool backFace)
        {
            BlockTexture texture = new BlockTexture();
            Vector2[] UVList = new Vector2[4];
            var vert = new VoxelVertex[4];
            Vector3 normal = voxel.Normal;
            if (voxel.Side == VoxelFace.Direction.Top) {}
            //switch (voxel.Side)
            //{
            //    case VoxelFace.Direction.Bottom:
            //        normal = - Vector3.UnitY;
            //        texture = TextureHelper.GetTexture(voxel.Type, BlockFaceDirection.YDecreasing);
            //        UVList = TextureHelper.GetUVMapping((int) texture, BlockFaceDirection.YDecreasing);
            //        break;
            //    case VoxelFace.Direction.Top:
            //        normal = Vector3.UnitY;
            //        texture = TextureHelper.GetTexture(voxel.Type, BlockFaceDirection.YIncreasing);
            //        UVList = TextureHelper.GetUVMapping((int) texture, BlockFaceDirection.YIncreasing);
            //        break;
            //    case VoxelFace.Direction.West:
            //        normal = Vector3.UnitX;
            //        texture = TextureHelper.GetTexture(voxel.Type, BlockFaceDirection.XIncreasing);
            //        UVList = TextureHelper.GetUVMapping((int) texture, BlockFaceDirection.XIncreasing);
            //        break;
            //    case VoxelFace.Direction.East:
            //        normal = -Vector3.UnitX;
            //        texture = TextureHelper.GetTexture(voxel.Type, BlockFaceDirection.XDecreasing);
            //        UVList = TextureHelper.GetUVMapping((int) texture, BlockFaceDirection.XDecreasing);
            //        break;
            //    case VoxelFace.Direction.North:
            //        normal = -Vector3.UnitZ;
            //        texture = TextureHelper.GetTexture(voxel.Type, BlockFaceDirection.ZDecreasing);
            //        UVList = TextureHelper.GetUVMapping((int) texture, BlockFaceDirection.ZDecreasing);
            //        break;
            //    case VoxelFace.Direction.South:
            //        normal = Vector3.UnitZ;
            //        texture = TextureHelper.GetTexture(voxel.Type, BlockFaceDirection.ZIncreasing);
            //        UVList = TextureHelper.GetUVMapping((int) texture, BlockFaceDirection.ZIncreasing);
            //        break;
            //}
            int ic = (Index.Count/6)*4;
            int[] indexes = !backFace ? new[] {2, 0, 1, 1, 3, 2} : new[] {2, 3, 1, 1, 0, 2};
            vert[0] = new VoxelVertex(bottomLeft*(VOXEL_SIZE), normal, UVList[0], new Vector3(voxel.Light, voxel.LightSettings[0], (int) (voxel.Side)));
            vert[1] = new VoxelVertex(bottomRight*(VOXEL_SIZE), normal, UVList[1], new Vector3(voxel.Light, voxel.LightSettings[1], (int) (voxel.Side)));
            vert[2] = new VoxelVertex(topLeft*(VOXEL_SIZE), normal, UVList[2], new Vector3(voxel.Light, voxel.LightSettings[2], (int) (voxel.Side)));
            vert[3] = new VoxelVertex(topRight*(VOXEL_SIZE), normal, UVList[3], new Vector3(voxel.Light, voxel.LightSettings[3], (int) (voxel.Side)));
            if (voxel.LightSettings[0] + voxel.LightSettings[3] > voxel.LightSettings[1] + voxel.LightSettings[2])
                indexes = !backFace ? new[] {0, 1, 3, 3, 2, 0} : new[] {0, 2, 3, 3, 1, 0};

            //int[] indexes = !backFace ? new[] { 0, 3, 2, 2, 1, 0 } : new[] { 0, 1, 2, 2, 0, 2 };
            Index.Add(indexes[0] + ic);
            Index.Add(indexes[1] + ic);
            Index.Add(indexes[2] + ic);
            Index.Add(indexes[3] + ic);
            Index.Add(indexes[4] + ic);
            Index.Add(indexes[5] + ic);
            vertices.Add(vert[0]);
            vertices.Add(vert[1]);
            vertices.Add(vert[2]);
            vertices.Add(vert[3]);
        }

        internal void Dispose()
        {
            throw new NotImplementedException();
        }
    }
}

【问题讨论】:

  • 您的代码示例非常抽象。它究竟打算完成什么?
  • 你有几个核心?如果你有一个 CPU 密集型应用程序,那么你的 CPU 密集型线程不应该多于处理器内核。否则你会在线程上下文切换上浪费很多时间。 100% CPU 不一定不好,只要这些周期主要用于处理,并且您的用户界面没有滞后。
  • 您可能想要查看 TPL 数据流而不是使用阻塞集合。它会更好地自动油门。
  • 我的主要问题是,如果使用 ThreadPool 与创建线程在架构意义中主要是正确的方法。不过,我可以给你一个上下文,这个类是由后台加载器的“另一个”线程调用的,所以当我需要加载一段数据时,它会尝试获取一个构建器实例并生成它。这实际上是在体素引擎中使用的,但由于它超出了范围,我真的很关心事物的设计/实现方面。我的问题是使用这样的线程池。我问这是不是要走的路。
  • “问题”,如果有的话,不是上面的代码,因为它所做的只是以迂回的方式创建线程。如果您想获得有关优化程序的建议,那么您应该发布占用 CPU 的代码。

标签: c# multithreading performance


【解决方案1】:

这在很大程度上取决于 ChunkBuilder 本身的安全性。如果它没有自己的依赖于块的状态(这是一个很好的设计),则不需要 builders 集合。你的所有流程看起来都很简单:

using System.Threading.Tasks;

...

public IEnumerable<Chunk> GetChunks()
{
    // Here you can return the whole chunks collection or yield them one by one.
    yield return new Chunk();
}

public void DoIt()
{
    ChunkBuilder builder = new ChunkBuilder();
    ParallelOptions options = new ParallelOptions() {
        MaxDegreeOfParallelism = 4
    };
    Parallel.ForEach(this.GetChunks(), options, chunk => builder.Generate(chunk));
}

如果你的 ChunkBuilder 包含块处理状态,那么有两种选择:

1)如果ChunkBuilder初始化简单快速,只需在需要处理时创建builder即可:

    Parallel.ForEach(this.GetChunks(), options, chunk => new ChunkBuilder().Generate(chunk));

2) 如果 ChunkBuilder 构建速度慢,最好根据 SRP 规则重新设计一点,将块处理状态提取到单独的对象中。

【讨论】:

    猜你喜欢
    • 2023-04-02
    • 2017-01-23
    • 1970-01-01
    • 1970-01-01
    • 2010-12-25
    • 2023-03-10
    • 2019-06-06
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多