首页 > 解决方案 > C# foreach 性能与内存碎片

问题描述

追踪性能问题(我知道微)我以这个测试程序结束。使用框架 4.5 和发布模式编译,它在我的机器上花费了大约 10 毫秒。

如果我删除这条线有什么困扰我

public int[] value1 = new int[80];

时间接近 2 毫秒。似乎存在一些内存碎片问题,但我未能解释原因。我已经使用 Net Core 2.0 测试了该程序,结果相同。谁能解释这种行为?

using System;
using System.Collections.Generic;
using System.Diagnostics;

namespace ConsoleApp4
{

    public class MyObject
    {
        public int value = 1;
        public int[] value1 = new int[80];
    }


    class Program
    {
        static void Main(string[] args)
        {

            var list = new List<MyObject>();
            for (int i = 0; i < 500000; i++)
            {
                list.Add(new MyObject());
            }

            long total = 0;
            for (int i = 0; i < 200; i++)
            {
                int counter = 0;
                Stopwatch timer = Stopwatch.StartNew();

                foreach (var obj in list)
                {
                    if (obj.value == 1)
                        counter++;
                }

                timer.Stop();
                total += timer.ElapsedMilliseconds;
            }

            Console.WriteLine(total / 200);

            Console.ReadKey();
        }
    }
}

更新:

经过一番研究,我得出结论,这只是处理器缓存访问时间。使用 VS 分析器,缓存未命中似乎要高得多

无数组

带数组

标签: c#performancememoryfragmentation

解决方案


有几个含义。

当你有你的 linepublic int[] value1 = new int[80];时,你有一个额外的内存分配:在一个堆上创建一个新数组,它将容纳 80 个整数(320 字节)+ 类的开销。你做了 500 000 次这样的分配。

这些分配总计超过 160 MB 的 RAM,这可能会导致 GC 启动并查看是否有要释放的内存。

此外,当您分配如此多的内存时,列表中的某些对象很可能不会保留在 CPU 缓存中。当您稍后枚举您的集合时,CPU 可能需要从 RAM 中读取数据,而不是从缓存中读取数据,这将导致严重的性能损失。


推荐阅读