迭代大列表并编辑所有列表项,而不会造成很多延迟

本文关键字:列表 延迟 编辑 迭代 | 更新日期: 2023-09-27 18:02:45

我在一个充满矩形的大列表中迭代每一帧它们都应该下降1个像素但由于矩形的数量太大,我得到了一个巨大的fps输入我的代码:

for (var x = 0; x < Water3.Count(); x++ )
{
    bool intersect = false;
    Rectangle rect = Water3[x];
    List<Rectangle> Water2 = new List<Rectangle>(Water3);
    Water2.RemoveAt(x);
    rect.Y++;
    foreach (Rectangle check in Water2)
    {
        if (check.IntersectsWith(rect))
        {
            intersect = true;
            break;
        }
    }
    if (rect.Y >= 699 || intersect == true)
    {
        rect.Y--;
    }
    Water[x] = rect;
    frameGraphics.FillRectangle(new SolidBrush(Color.Red), Water3[x]);
}

这是我现在的代码:

private void render()
{
    int framesRendered = 0;
    long startTime = Environment.TickCount;
    Bitmap frame = new Bitmap(Game.CANVAS_WIDTH, Game.CANVAS_HEIGHT);
    Graphics frameGraphics = Graphics.FromImage(frame);

    #region Brushes
    SolidBrush Black = new SolidBrush(Color.Black);
    SolidBrush Red = new SolidBrush(Color.Red);
    #endregion 
    while (true)
    {
        frameGraphics.FillRectangle(Black, 0, 0, Game.CANVAS_WIDTH, Game.CANVAS_HEIGHT);
        List<Rectangle> Water3 = new List<Rectangle>(Water);
        for (var x = 0; x < Water3.Count; x++)
        {
            Rectangle rect = Water3[x];
            rect.Y++;
            bool intersect = Water3.Where((t, i) => i != x).Any(check => check.IntersectsWith(rect));
            if (rect.Y >= 699 || intersect)
                rect.Y--;
            Water[x] = rect;
            frameGraphics.FillRectangle(Red, Water3[x]);
        }
        drawHandle.DrawImage(frame, 0, 0);
        //benchmarking
        framesRendered++;
        if (Environment.TickCount >= startTime + 1000)
        {
            Console.WriteLine("Engine: " + framesRendered + " fps");
            framesRendered = 0;
            startTime = Environment.TickCount;

迭代大列表并编辑所有列表项,而不会造成很多延迟

你在循环中一次又一次地复制列表:

List<Rectangle> Water2 = new List<Rectangle>(Water3);

这将给你一个巨大的n^2的命中率

正如Yishai已经说过的,你在每个循环中创建了一个巨大的列表Water3。这就是在每个周期中分配和释放大量内存,这会对性能产生巨大影响。所以最好使用你已有的列表。

        for (var x = 0; x < Water3.Count; x++)
        {
            bool intersect = false;
            Rectangle rect = Water3[x];
            rect.Y++;
            for (var i = 0; i < Water3.Count; i++)
            {
                if (i == x)
                    continue;
                Rectangle check = Water3[i];
                if (check.IntersectsWith(rect))
                {
                    intersect = true;
                    break;
                }
            }
            if (rect.Y >= 699 || intersect)
                rect.Y--;
            Water[x] = rect;
            // painting 1000 rects like this takes about 12msec
            // frameGraphics.FillRectangle(Brushes.Red, rect);
        }
        // painting all 1000 rects at once will take only 3msec
        frameGraphics.FillRectangles(Brushes.Red, Water.ToArray());

或者使用LINQ表达式:

        for (var x = 0; x < Water3.Count; x++)
        {
            Rectangle rect = Water3[x];
            rect.Y++;
            bool intersect = Water3.Where((t, i) => i != x).Any(check => check.IntersectsWith(rect));
            if (rect.Y >= 699 || intersect)
                rect.Y--;
            Water[x] = rect;
            // painting 1000 rects like this takes about 12msec
            // frameGraphics.FillRectangle(Brushes.Red, rect);
        }
        // painting all 1000 rects at once will take only 3msec
        frameGraphics.FillRectangles(Brushes.Red, Water.ToArray());

另一个关于内存泄漏的评论。你不应该在循环中一遍又一遍地创建SolidBrush。而是在循环之外创建笔刷,并将其放入using语句中,以便之后处理它。如果你需要一个典型的颜色-像你:红色-然后不要创建自己的画笔,而是使用静态成员刷子。红色相反。