using BenchmarkDotNet.Attributes;
namespace Apq.Cfg.Benchmarks;
///
/// 批量操作性能基准测试
/// 测试 GetMany/SetMany 与单次操作的性能对比
///
[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}\": \"{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 对比
///
/// 使用 GetMany 批量获取 10 个键
///
[Benchmark(Baseline = true)]
[BenchmarkCategory("GetMany")]
public IReadOnlyDictionary GetMany_10Keys()
{
return _cfg.GetMany(_keys10);
}
///
/// 使用单次 Get 循环获取 10 个键
///
[Benchmark]
[BenchmarkCategory("GetMany")]
public void Get_Loop_10Keys()
{
foreach (var key in _keys10)
{
_ = _cfg.Get(key);
}
}
///
/// 使用 GetMany 批量获取 50 个键
///
[Benchmark]
[BenchmarkCategory("GetMany")]
public IReadOnlyDictionary GetMany_50Keys()
{
return _cfg.GetMany(_keys50);
}
///
/// 使用单次 Get 循环获取 50 个键
///
[Benchmark]
[BenchmarkCategory("GetMany")]
public void Get_Loop_50Keys()
{
foreach (var key in _keys50)
{
_ = _cfg.Get(key);
}
}
///
/// 使用 GetMany 批量获取 100 个键
///
[Benchmark]
[BenchmarkCategory("GetMany")]
public IReadOnlyDictionary GetMany_100Keys()
{
return _cfg.GetMany(_keys100);
}
///
/// 使用单次 Get 循环获取 100 个键
///
[Benchmark]
[BenchmarkCategory("GetMany")]
public void Get_Loop_100Keys()
{
foreach (var key in _keys100)
{
_ = _cfg.Get(key);
}
}
#endregion
#region GetMany 回调方式(高性能)
///
/// 使用 GetMany 回调方式批量获取 10 个键(零分配)
///
[Benchmark]
[BenchmarkCategory("GetManyCallback")]
public void GetMany_Callback_10Keys()
{
_cfg.GetMany(_keys10, (key, value) => { _ = value; });
}
///
/// 使用 GetMany 回调方式批量获取 50 个键(零分配)
///
[Benchmark]
[BenchmarkCategory("GetManyCallback")]
public void GetMany_Callback_50Keys()
{
_cfg.GetMany(_keys50, (key, value) => { _ = value; });
}
///
/// 使用 GetMany 回调方式批量获取 100 个键(零分配)
///
[Benchmark]
[BenchmarkCategory("GetManyCallback")]
public void GetMany_Callback_100Keys()
{
_cfg.GetMany(_keys100, (key, value) => { _ = value; });
}
#endregion
#region GetMany 类型转换批量获取
///
/// 使用 GetMany 批量获取并转换类型
///
[Benchmark]
[BenchmarkCategory("GetManyTyped")]
public void GetMany_Typed_10Keys()
{
_ = _cfg.GetMany(_keys10);
}
///
/// 使用单次 Get 循环获取并转换类型
///
[Benchmark]
[BenchmarkCategory("GetManyTyped")]
public void Get_Typed_Loop_10Keys()
{
foreach (var key in _keys10)
{
_ = _cfg.Get(key);
}
}
#endregion
#region SetMany vs 单次 Set 对比
///
/// 使用 SetMany 批量设置 10 个键
///
[Benchmark]
[BenchmarkCategory("SetMany")]
public void SetMany_10Keys()
{
var values = new Dictionary();
for (int i = 0; i < 10; i++)
{
values[$"Batch:Key{i}"] = $"Value{i}";
}
_cfg.SetMany(values);
}
///
/// 使用单次 Set 循环设置 10 个键
///
[Benchmark]
[BenchmarkCategory("SetMany")]
public void Set_Loop_10Keys()
{
for (int i = 0; i < 10; i++)
{
_cfg.Set($"Loop:Key{i}", $"Value{i}");
}
}
///
/// 使用 SetMany 批量设置 50 个键
///
[Benchmark]
[BenchmarkCategory("SetMany")]
public void SetMany_50Keys()
{
var values = new Dictionary();
for (int i = 0; i < 50; i++)
{
values[$"Batch50:Key{i}"] = $"Value{i}";
}
_cfg.SetMany(values);
}
///
/// 使用单次 Set 循环设置 50 个键
///
[Benchmark]
[BenchmarkCategory("SetMany")]
public void Set_Loop_50Keys()
{
for (int i = 0; i < 50; i++)
{
_cfg.Set($"Loop50:Key{i}", $"Value{i}");
}
}
///
/// 使用 SetMany 批量设置 100 个键
///
[Benchmark]
[BenchmarkCategory("SetMany")]
public void SetMany_100Keys()
{
var values = new Dictionary();
for (int i = 0; i < 100; i++)
{
values[$"Batch100:Key{i}"] = $"Value{i}";
}
_cfg.SetMany(values);
}
///
/// 使用单次 Set 循环设置 100 个键
///
[Benchmark]
[BenchmarkCategory("SetMany")]
public void Set_Loop_100Keys()
{
for (int i = 0; i < 100; i++)
{
_cfg.Set($"Loop100:Key{i}", $"Value{i}");
}
}
#endregion
#region 混合读写场景
///
/// 批量读取后批量写入
///
[Benchmark]
[BenchmarkCategory("Mixed")]
public void BatchRead_ThenBatchWrite()
{
// 批量读取
var values = _cfg.GetMany(_keys10);
// 批量写入(修改后的值)
var newValues = new Dictionary();
foreach (var kv in values)
{
newValues[kv.Key + "_copy"] = kv.Value + "_modified";
}
_cfg.SetMany(newValues);
}
///
/// 单次读取后单次写入(循环)
///
[Benchmark]
[BenchmarkCategory("Mixed")]
public void LoopRead_ThenLoopWrite()
{
// 循环读取
var values = new Dictionary();
foreach (var key in _keys10)
{
values[key] = _cfg.Get(key);
}
// 循环写入
foreach (var kv in values)
{
_cfg.Set(kv.Key + "_copy2", kv.Value + "_modified");
}
}
#endregion
}