Bläddra i källkod

性能测试全面覆盖

黄中银 2 veckor sedan
förälder
incheckning
bf5d544602
2 ändrade filer med 348 tillägg och 16 borttagningar
  1. 304 0
      benchmarks/Apq.Cfg.Benchmarks/BatchOperationBenchmarks.cs
  2. 44 16
      tests/README.md

+ 304 - 0
benchmarks/Apq.Cfg.Benchmarks/BatchOperationBenchmarks.cs

@@ -0,0 +1,304 @@
+using BenchmarkDotNet.Attributes;
+
+namespace Apq.Cfg.Benchmarks;
+
+/// <summary>
+/// 批量操作性能基准测试
+/// 测试 GetMany/SetMany 与单次操作的性能对比
+/// </summary>
+[Config(typeof(BenchmarkConfig))]
+public class BatchOperationBenchmarks : IDisposable
+{
+    private readonly string _testDir;
+    private ICfgRoot _cfg = null!;
+
+    // 预定义的键
+    private readonly string[] _keys10 = new string[10];
+    private readonly string[] _keys50 = new string[50];
+    private readonly string[] _keys100 = new string[100];
+
+    public BatchOperationBenchmarks()
+    {
+        _testDir = Path.Combine(Path.GetTempPath(), $"ApqCfgBench_{Guid.NewGuid():N}");
+        Directory.CreateDirectory(_testDir);
+
+        // 预生成键
+        for (int i = 0; i < 100; i++)
+        {
+            var key = $"Data:Key{i}";
+            if (i < 10) _keys10[i] = key;
+            if (i < 50) _keys50[i] = key;
+            _keys100[i] = key;
+        }
+    }
+
+    [GlobalSetup]
+    public void Setup()
+    {
+        var jsonPath = Path.Combine(_testDir, "config.json");
+
+        // 生成包含 100 个键的配置
+        var content = "{\n  \"Data\": {\n";
+        for (int i = 0; i < 100; i++)
+        {
+            content += $"    \"Key{i}\": \"Value{i}\"";
+            if (i < 99) content += ",";
+            content += "\n";
+        }
+        content += "  }\n}";
+
+        File.WriteAllText(jsonPath, content);
+
+        _cfg = new CfgBuilder()
+            .AddJson(jsonPath, level: 0, writeable: true, isPrimaryWriter: true)
+            .Build();
+    }
+
+    [GlobalCleanup]
+    public void Cleanup()
+    {
+        Dispose();
+    }
+
+    public void Dispose()
+    {
+        _cfg?.Dispose();
+
+        if (Directory.Exists(_testDir))
+        {
+            Directory.Delete(_testDir, true);
+        }
+    }
+
+    #region GetMany vs 单次 Get 对比
+
+    /// <summary>
+    /// 使用 GetMany 批量获取 10 个键
+    /// </summary>
+    [Benchmark(Baseline = true)]
+    [BenchmarkCategory("GetMany")]
+    public IReadOnlyDictionary<string, string?> GetMany_10Keys()
+    {
+        return _cfg.GetMany(_keys10);
+    }
+
+    /// <summary>
+    /// 使用单次 Get 循环获取 10 个键
+    /// </summary>
+    [Benchmark]
+    [BenchmarkCategory("GetMany")]
+    public void Get_Loop_10Keys()
+    {
+        foreach (var key in _keys10)
+        {
+            _ = _cfg.Get(key);
+        }
+    }
+
+    /// <summary>
+    /// 使用 GetMany 批量获取 50 个键
+    /// </summary>
+    [Benchmark]
+    [BenchmarkCategory("GetMany")]
+    public IReadOnlyDictionary<string, string?> GetMany_50Keys()
+    {
+        return _cfg.GetMany(_keys50);
+    }
+
+    /// <summary>
+    /// 使用单次 Get 循环获取 50 个键
+    /// </summary>
+    [Benchmark]
+    [BenchmarkCategory("GetMany")]
+    public void Get_Loop_50Keys()
+    {
+        foreach (var key in _keys50)
+        {
+            _ = _cfg.Get(key);
+        }
+    }
+
+    /// <summary>
+    /// 使用 GetMany 批量获取 100 个键
+    /// </summary>
+    [Benchmark]
+    [BenchmarkCategory("GetMany")]
+    public IReadOnlyDictionary<string, string?> GetMany_100Keys()
+    {
+        return _cfg.GetMany(_keys100);
+    }
+
+    /// <summary>
+    /// 使用单次 Get 循环获取 100 个键
+    /// </summary>
+    [Benchmark]
+    [BenchmarkCategory("GetMany")]
+    public void Get_Loop_100Keys()
+    {
+        foreach (var key in _keys100)
+        {
+            _ = _cfg.Get(key);
+        }
+    }
+
+    #endregion
+
+    #region GetMany<T> 类型转换批量获取
+
+    /// <summary>
+    /// 使用 GetMany<int> 批量获取并转换类型
+    /// </summary>
+    [Benchmark]
+    [BenchmarkCategory("GetManyTyped")]
+    public void GetMany_Typed_10Keys()
+    {
+        _ = _cfg.GetMany<int>(_keys10);
+    }
+
+    /// <summary>
+    /// 使用单次 Get<int> 循环获取并转换类型
+    /// </summary>
+    [Benchmark]
+    [BenchmarkCategory("GetManyTyped")]
+    public void Get_Typed_Loop_10Keys()
+    {
+        foreach (var key in _keys10)
+        {
+            _ = _cfg.Get<int>(key);
+        }
+    }
+
+    #endregion
+
+    #region SetMany vs 单次 Set 对比
+
+    /// <summary>
+    /// 使用 SetMany 批量设置 10 个键
+    /// </summary>
+    [Benchmark]
+    [BenchmarkCategory("SetMany")]
+    public void SetMany_10Keys()
+    {
+        var values = new Dictionary<string, string?>();
+        for (int i = 0; i < 10; i++)
+        {
+            values[$"Batch:Key{i}"] = $"Value{i}";
+        }
+        _cfg.SetMany(values);
+    }
+
+    /// <summary>
+    /// 使用单次 Set 循环设置 10 个键
+    /// </summary>
+    [Benchmark]
+    [BenchmarkCategory("SetMany")]
+    public void Set_Loop_10Keys()
+    {
+        for (int i = 0; i < 10; i++)
+        {
+            _cfg.Set($"Loop:Key{i}", $"Value{i}");
+        }
+    }
+
+    /// <summary>
+    /// 使用 SetMany 批量设置 50 个键
+    /// </summary>
+    [Benchmark]
+    [BenchmarkCategory("SetMany")]
+    public void SetMany_50Keys()
+    {
+        var values = new Dictionary<string, string?>();
+        for (int i = 0; i < 50; i++)
+        {
+            values[$"Batch50:Key{i}"] = $"Value{i}";
+        }
+        _cfg.SetMany(values);
+    }
+
+    /// <summary>
+    /// 使用单次 Set 循环设置 50 个键
+    /// </summary>
+    [Benchmark]
+    [BenchmarkCategory("SetMany")]
+    public void Set_Loop_50Keys()
+    {
+        for (int i = 0; i < 50; i++)
+        {
+            _cfg.Set($"Loop50:Key{i}", $"Value{i}");
+        }
+    }
+
+    /// <summary>
+    /// 使用 SetMany 批量设置 100 个键
+    /// </summary>
+    [Benchmark]
+    [BenchmarkCategory("SetMany")]
+    public void SetMany_100Keys()
+    {
+        var values = new Dictionary<string, string?>();
+        for (int i = 0; i < 100; i++)
+        {
+            values[$"Batch100:Key{i}"] = $"Value{i}";
+        }
+        _cfg.SetMany(values);
+    }
+
+    /// <summary>
+    /// 使用单次 Set 循环设置 100 个键
+    /// </summary>
+    [Benchmark]
+    [BenchmarkCategory("SetMany")]
+    public void Set_Loop_100Keys()
+    {
+        for (int i = 0; i < 100; i++)
+        {
+            _cfg.Set($"Loop100:Key{i}", $"Value{i}");
+        }
+    }
+
+    #endregion
+
+    #region 混合读写场景
+
+    /// <summary>
+    /// 批量读取后批量写入
+    /// </summary>
+    [Benchmark]
+    [BenchmarkCategory("Mixed")]
+    public void BatchRead_ThenBatchWrite()
+    {
+        // 批量读取
+        var values = _cfg.GetMany(_keys10);
+
+        // 批量写入(修改后的值)
+        var newValues = new Dictionary<string, string?>();
+        foreach (var kv in values)
+        {
+            newValues[kv.Key + "_copy"] = kv.Value + "_modified";
+        }
+        _cfg.SetMany(newValues);
+    }
+
+    /// <summary>
+    /// 单次读取后单次写入(循环)
+    /// </summary>
+    [Benchmark]
+    [BenchmarkCategory("Mixed")]
+    public void LoopRead_ThenLoopWrite()
+    {
+        // 循环读取
+        var values = new Dictionary<string, string?>();
+        foreach (var key in _keys10)
+        {
+            values[key] = _cfg.Get(key);
+        }
+
+        // 循环写入
+        foreach (var kv in values)
+        {
+            _cfg.Set(kv.Key + "_copy2", kv.Value + "_modified");
+        }
+    }
+
+    #endregion
+}

+ 44 - 16
tests/README.md

@@ -25,7 +25,7 @@ dotnet test tests/Apq.Cfg.Tests.Net9/
 dotnet test --filter "FullyQualifiedName~JsonCfgTests"
 ```
 
-## 测试统计(共 199 个测试)
+## 测试统计(共 253 个测试)
 
 | 测试类 | 测试数量 | 说明 |
 |--------|----------|------|
@@ -37,17 +37,18 @@ dotnet test --filter "FullyQualifiedName~JsonCfgTests"
 | TomlCfgTests | 6 | TOML 文件配置源测试 |
 | RedisCfgTests | 5 | Redis 配置源测试 |
 | DatabaseCfgTests | 5 | 数据库配置源测试 |
-| CfgRootExtensionsTests | 4 | 扩展方法测试 |
+| CfgRootExtensionsTests | 4 | 扩展方法测试(TryGet/GetRequired)|
 | CfgBuilderAdvancedTests | 14 | 高级功能测试 |
-| DynamicReloadTests | 12 | 动态配置重载测试 |
+| DynamicReloadTests | 22 | 动态配置重载测试 |
 | EncodingDetectionTests | 14 | 编码检测测试 |
-| ConcurrencyTests | 10 | 并发安全测试 |
-| BoundaryConditionTests | 32 | 边界条件测试 |
-| ExceptionHandlingTests | 20 | 异常处理测试 |
+| ConcurrencyTests | 9 | 并发安全测试 |
+| BoundaryConditionTests | 25 | 边界条件测试 |
+| ExceptionHandlingTests | 18 | 异常处理测试 |
 | ConfigChangesSubscriptionTests | 28 | 配置变更订阅测试 |
-| CfgSectionTests | 14 | 配置节(GetSection/GetChildKeys)测试 |
-| ServiceCollectionExtensionsTests | 10 | 依赖注入扩展测试 |
-| EncodingTests | 14 | 编码映射测试 |
+| CfgSectionTests | 13 | 配置节(GetSection/GetChildKeys/GetOrDefault)测试 |
+| ServiceCollectionExtensionsTests | 11 | 依赖注入扩展测试 |
+| EncodingTests | 22 | 编码映射测试 |
+| PerformanceOptimizationTests | 22 | 性能优化测试(GetMany/SetMany/缓存)|
 
 ## 公开 API 覆盖矩阵
 
@@ -85,6 +86,7 @@ dotnet test --filter "FullyQualifiedName~JsonCfgTests"
 | **CfgRootExtensions** |
 | `TryGet<T>()` | ✅ | - | - | - | - | - | - | - |
 | `GetRequired<T>()` | ✅ | - | - | - | - | - | - | - |
+| `GetOrDefault<T>()` | ✅ | - | - | - | - | - | - | - |
 | **FileCfgSourceBase** |
 | `EncodingDetector` | ✅ | - | - | - | - | - | - | - |
 | `EncodingConfidenceThreshold` | ✅ | - | - | - | - | - | - | - |
@@ -110,14 +112,40 @@ dotnet test --filter "FullyQualifiedName~JsonCfgTests"
 | 基本读写 | JsonCfgTests, 各格式测试 | 47 |
 | 类型转换 | JsonCfgTests | 15 |
 | 编码检测 | EncodingDetectionTests | 14 |
-| 编码映射 | EncodingTests | 14 |
-| 并发安全 | ConcurrencyTests | 10 |
-| 边界条件 | BoundaryConditionTests | 32 |
-| 异常处理 | ExceptionHandlingTests | 20 |
-| 动态重载 | DynamicReloadTests | 12 |
+| 编码映射 | EncodingTests | 22 |
+| 并发安全 | ConcurrencyTests | 9 |
+| 边界条件 | BoundaryConditionTests | 25 |
+| 异常处理 | ExceptionHandlingTests | 18 |
+| 动态重载 | DynamicReloadTests | 22 |
 | 变更订阅 | ConfigChangesSubscriptionTests | 28 |
-| 配置节访问 | CfgSectionTests | 14 |
-| 依赖注入 | ServiceCollectionExtensionsTests | 10 |
+| 配置节访问 | CfgSectionTests | 13 |
+| 依赖注入 | ServiceCollectionExtensionsTests | 11 |
+| 批量操作 | PerformanceOptimizationTests | 22 |
+
+## 性能基准测试
+
+性能基准测试位于 `benchmarks/Apq.Cfg.Benchmarks/` 目录,使用 BenchmarkDotNet 框架。
+
+| 基准测试文件 | 说明 |
+|--------------|------|
+| ReadWriteBenchmarks | 不同配置源的 Get/Set/Exists 性能对比 |
+| CacheBenchmarks | 缓存效果测试(热路径、缓存命中/未命中)|
+| TypeConversionBenchmarks | 类型转换性能测试 |
+| ConcurrencyBenchmarks | 并发读写性能测试 |
+| GetSectionBenchmarks | GetSection/GetChildKeys 性能测试 |
+| SaveBenchmarks | SaveAsync 持久化性能测试 |
+| RemoveBenchmarks | Remove 操作性能测试 |
+| MultiSourceBenchmarks | 多配置源合并性能测试 |
+| LargeFileBenchmarks | 大文件配置性能测试 |
+| KeyPathBenchmarks | 键路径解析性能测试 |
+| BatchOperationBenchmarks | GetMany/SetMany 批量操作性能测试 |
+
+运行性能测试:
+
+```bash
+cd benchmarks/Apq.Cfg.Benchmarks
+dotnet run -c Release
+```
 
 ## 测试覆盖率