基于杨中科老师的GC示例,写一个Benchmark

很久之前,我看到杨中科老师的一篇文章
谁说.NET没有GC调优?只改一行代码就让程序不再占用内存受益匪浅。

最近遇到了类似的问题,不过细节有点忘了,于是又回顾了一下,顺便写了一个Benchmark,加强了一下印象。


    // ==================== 数据类型定义 ====================

    // 1. 原始 Class 定义
    public class WeatherForecastClass
    {
        public DateOnly Date { get; set; }
        public int TemperatureC { get; set; }
        public int TemperatureF => 32 + (int)(TemperatureC / 0.5556);
        public string Summary { get; set; }
    }

    // 2. 优化方案二:Struct 定义
    public struct WeatherForecastStruct
    {
        public DateOnly Date { get; set; }
        public int TemperatureC { get; set; }
        public int TemperatureF => 32 + (int)(TemperatureC / 0.5556);
        public string Summary { get; set; }
    }

    // ==================== 基准测试类 ====================
    [MemoryDiagnoser] // 开启内存诊断,关注内存分配和GC次数
    public class ToArrayContext
    {
        private static readonly string[] Summaries = new[]
        {
            "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
            };

        // 模拟文中提到的 150,000 条数据
        private const int Count = 150_000;

        /// <summary>
        /// 方案一(基线):Class + ToArray()
        /// 问题:产生大对象(LOH),内存占用高,GC压力大
        /// </summary>
        [Benchmark(Baseline = true)]
        public WeatherForecastClass[] GetClassToArray()
        {
            return Enumerable.Range(1, Count).Select(index => new WeatherForecastClass
            {
                Date = DateOnly.FromDateTime(DateTime.Now.AddDays(index)),
                TemperatureC = Random.Shared.Next(-20, 55),
                Summary = Summaries[Random.Shared.Next(Summaries.Length)]
            }).ToArray();
        }

        /// <summary>
        /// 方案一 的 List 对照
        /// </summary>
        [Benchmark]
        public List<WeatherForecastClass> GetClassToList()
        {
            return Enumerable.Range(1, Count).Select(index => new WeatherForecastClass
            {
                Date = DateOnly.FromDateTime(DateTime.Now.AddDays(index)),
                TemperatureC = Random.Shared.Next(-20, 55),
                Summary = Summaries[Random.Shared.Next(Summaries.Length)]
            }).ToList();
        }

        /// <summary>
        /// 方案一优化:Class + 流式返回 (IEnumerable)
        /// 优势:没有 ToArray() 产生的数组对象,流水线作业,内存占用极低
        /// 注意:为了在 Benchmark 中公平比较“执行完整逻辑”,这里强制迭代一次模拟消费过程。
        /// </summary>
        [Benchmark]
        public void GetClassStream()
        {
            var result = Enumerable.Range(1, Count).Select(index => new WeatherForecastClass
            {
                Date = DateOnly.FromDateTime(DateTime.Now.AddDays(index)),
                TemperatureC = Random.Shared.Next(-20, 55),
                Summary = Summaries[Random.Shared.Next(Summaries.Length)]
            });

            // 模拟消费者(如 JSON 序列化器)逐条读取数据
            // 只有迭代才会真正创建对象
            foreach (var _ in result) { }
        }

        /// <summary>
        /// 方案二优化:Struct + ToArray()
        /// 优势:Struct 内存布局更紧凑,无对象头开销,数组占用内存减半
        /// 注意:虽然内存占用减半,但仍然有一次 ToArray() 的大块内存分配
        /// </summary>
        [Benchmark]
        public WeatherForecastStruct[] GetStructToArray()
        {
            return Enumerable.Range(1, Count).Select(index => new WeatherForecastStruct
            {
                Date = DateOnly.FromDateTime(DateTime.Now.AddDays(index)),
                TemperatureC = Random.Shared.Next(-20, 55),
                Summary = Summaries[Random.Shared.Next(Summaries.Length)]
            }).ToArray();
        }
    }

    // ==================== 程序入口 ====================
    //public class Program
    //{
    //    public static void Main(string[] args)
    //    {
    //        // 运行基准测试
    //        var summary = BenchmarkRunner.Run<ToArrayContext>();
    //    }
    //}

运行结果如下:


BenchmarkDotNet=v0.13.5, OS=Windows 10 (10.0.19045.6466/22H2/2022Update)
Intel Core i7-9700 CPU 3.00GHz, 1 CPU, 8 logical and 8 physical cores
.NET SDK=8.0.200
  [Host]     : .NET 8.0.11 (8.0.1124.51707), X64 RyuJIT AVX2
  DefaultJob : .NET 8.0.11 (8.0.1124.51707), X64 RyuJIT AVX2


Method Mean Error StdDev Ratio RatioSD Gen0 Gen1 Gen2 Allocated Alloc Ratio
GetClassToArray 13.504 ms 0.2631 ms 0.3132 ms 1.00 0.00 1000.0000 718.7500 296.8750 5.72 MB 1.00
GetClassToList 13.169 ms 0.1357 ms 0.1269 ms 0.97 0.03 1000.0000 718.7500 296.8750 5.72 MB 1.00
GetClassStream 5.977 ms 0.0235 ms 0.0196 ms 0.44 0.01 757.8125 - - 4.58 MB 0.80
GetStructToArray 5.488 ms 0.0162 ms 0.0151 ms 0.41 0.01 328.1250 328.1250 328.1250 2.29 MB 0.40
posted @ 2026-03-06 14:39  cnblogsName  阅读(11)  评论(0)    收藏  举报