You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用BenchmarkDotNet实现预填充集合的性能测试用例

解决BenchmarkDotNet集合插入性能测试的隔离问题

针对你的需求,核心思路是一次性预生成所有初始大小的源集合并缓存,每次基准测试时复用缓存的源集合,通过副本或利用不可变特性保证测试隔离,既避免重复生成大集合的耗时,又能确保每个测试用例的初始状态干净。下面是具体实现方案:

方案1:缓存源集合+基准方法内创建副本(适用于复制开销可忽略的场景)

先在GlobalSetup中一次性生成所有需要的初始大小的集合并缓存,每次基准测试时从缓存取出源集合,创建副本后执行插入操作,这样源集合不会被修改,每个测试都是独立的。

代码示例

using BenchmarkDotNet.Attributes;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;

[MemoryDiagnoser]
public class CollectionInsertBenchmarks
{
    // 定义要测试的初始集合大小参数
    [Params(0, 10, 100, 1000, 10000, 100000, 1000000, 10000000)] // 1e8内存压力过大,建议酌情测试
    public int InitialSize { get; set; }

    // 缓存不同初始大小的源集合
    private Dictionary<int, SortedDictionary<int, int>> _sortedDictCache;
    private Dictionary<int, SortedList<int, int>> _sortedListCache;
    private Dictionary<int, ImmutableSortedDictionary<int, int>> _immutableDictCache;

    // 预生成固定的10个插入项(用固定种子保证测试一致性)
    private int[] _itemsToInsert;

    [GlobalSetup]
    public void GlobalSetup()
    {
        // 预生成插入的随机项,固定种子避免每次测试结果波动
        var random = new Random(42);
        _itemsToInsert = Enumerable.Range(0, 10).Select(_ => random.Next(int.MaxValue)).ToArray();

        // 初始化缓存,每个初始大小只生成一次源集合
        _sortedDictCache = new Dictionary<int, SortedDictionary<int, int>>();
        _sortedListCache = new Dictionary<int, SortedList<int, int>>();
        _immutableDictCache = new Dictionary<int, ImmutableSortedDictionary<int, int>>();

        var testSizes = new[] { 0, 10, 100, 1000, 10000, 100000, 1000000, 10000000 };
        foreach (var size in testSizes)
        {
            // 生成SortedDictionary源集合
            var sortedDict = new SortedDictionary<int, int>();
            for (int i = 0; i < size; i++)
                sortedDict.Add(i, i);
            _sortedDictCache[size] = sortedDict;

            // 生成SortedList源集合
            var sortedList = new SortedList<int, int>();
            for (int i = 0; i < size; i++)
                sortedList.Add(i, i);
            _sortedListCache[size] = sortedList;

            // 生成ImmutableSortedDictionary源集合
            var immutableDict = ImmutableSortedDictionary<int, int>.Empty;
            for (int i = 0; i < size; i++)
                immutableDict = immutableDict.Add(i, i);
            _immutableDictCache[size] = immutableDict;
        }
    }

    [Benchmark]
    public SortedDictionary<int, int> SortedDictionary_Insert()
    {
        // 从缓存取源集合,创建副本(避免修改源缓存)
        var workingDict = new SortedDictionary<int, int>(_sortedDictCache[InitialSize]);
        foreach (var item in _itemsToInsert)
            workingDict.Add(item, item);
        return workingDict;
    }

    [Benchmark]
    public SortedList<int, int> SortedList_Insert()
    {
        var workingList = new SortedList<int, int>(_sortedListCache[InitialSize]);
        foreach (var item in _itemsToInsert)
            workingList.Add(item, item);
        return workingList;
    }

    [Benchmark]
    public ImmutableSortedDictionary<int, int> ImmutableSortedDictionary_Insert()
    {
        // 不可变集合无需复制,插入会返回新实例,源缓存不受影响
        var workingDict = _immutableDictCache[InitialSize];
        foreach (var item in _itemsToInsert)
            workingDict = workingDict.Add(item, item);
        return workingDict;
    }
}

方案2:缓存源集合+IterationSetup复制(适用于复制开销不可忽略的场景)

如果集合太大,复制操作的时间会影响插入性能的测量,就把复制操作放到[IterationSetup]中(这个阶段的操作不会被计入基准测试的时间),基准方法只执行插入逻辑,保证测量结果纯粹。

代码示例(仅修改可变集合的测试部分)

// 在类中新增实例字段,存储每次迭代的工作集合
private SortedDictionary<int, int> _currentSortedDict;
private SortedList<int, int> _currentSortedList;

[IterationSetup]
public void IterationSetup()
{
    // 每次迭代前复制源集合,该操作不计入基准时间
    _currentSortedDict = new SortedDictionary<int, int>(_sortedDictCache[InitialSize]);
    _currentSortedList = new SortedList<int, int>(_sortedListCache[InitialSize]);
}

[Benchmark]
public void SortedDictionary_Insert()
{
    // 只测量插入操作的时间
    foreach (var item in _itemsToInsert)
        _currentSortedDict.Add(item, item);
}

[Benchmark]
public void SortedList_Insert()
{
    foreach (var item in _itemsToInsert)
        _currentSortedList.Add(item, item);
}

关键优势

  1. 源集合只生成一次:所有初始大小的集合在GlobalSetup中一次性生成,避免重复创建大集合的耗时
  2. 测试完全隔离:可变集合通过副本保证每次测试的初始状态干净,不可变集合利用自身特性天然隔离
  3. 结果稳定:预生成固定的插入项,避免随机数生成对测试结果的干扰

内容的提问来源于stack exchange,提问作者Iaman Swtrse

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.20 11:24:52