using System.IO;
using BenchmarkDotNet.Attributes;
using NewLife.Buffers;
using NewLife.Serialization;
namespace Benchmark.SerializerBenchmarks;
// -------------------------------------------------------
// 测试用模型定义
// -------------------------------------------------------
/// <summary>普通 POCO:通过反射路径序列化</summary>
public class SimpleModel
{
public Int32 Id { get; set; }
public String Name { get; set; } = String.Empty;
public Boolean IsActive { get; set; }
public DateTime CreatedAt { get; set; }
public Double Score { get; set; }
}
/// <summary>嵌套 POCO:含引用类型成员</summary>
public class NestedModel
{
public Int32 Id { get; set; }
public String Title { get; set; } = String.Empty;
public SimpleModel? Inner { get; set; }
public Int64 Timestamp { get; set; }
}
/// <summary>ISpanSerializable 实现:零反射快速路径</summary>
public class FastModel : ISpanSerializable
{
public Int32 Id { get; set; }
public String Name { get; set; } = String.Empty;
public Boolean IsActive { get; set; }
public DateTime CreatedAt { get; set; }
public Double Score { get; set; }
private static readonly DateTime _dt1970 = new(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
/// <summary>将对象成员序列化写入SpanWriter</summary>
/// <param name="writer">Span写入器</param>
public void Write(ref SpanWriter writer)
{
writer.Write(Id);
writer.Write(Name, 0);
writer.Write((Byte)(IsActive ? 1 : 0));
writer.Write(CreatedAt > DateTime.MinValue ? (UInt32)(CreatedAt - _dt1970).TotalSeconds : 0u);
writer.Write(Score);
}
/// <summary>从SpanReader反序列化读取对象成员</summary>
/// <param name="reader">Span读取器</param>
public void Read(ref SpanReader reader)
{
Id = reader.ReadInt32();
Name = reader.ReadString() ?? String.Empty;
IsActive = reader.ReadByte() != 0;
var sec = reader.ReadUInt32();
CreatedAt = sec == 0 ? DateTime.MinValue : _dt1970.AddSeconds(sec);
Score = reader.ReadDouble();
}
}
// -------------------------------------------------------
// 序列化基准测试
// -------------------------------------------------------
/// <summary>SpanSerializer vs Binary 序列化性能对比</summary>
/// <remarks>
/// 三条测试路径:
/// <list type="bullet">
/// <item>SpanSerializer(反射) — 普通 POCO,通过编译委托序列化</item>
/// <item>SpanSerializer(ISpanSerializable) — 实现接口,零反射快速路径</item>
/// <item>Binary — 基于 Stream 的传统序列化</item>
/// </list>
/// </remarks>
[MemoryDiagnoser]
[SimpleJob]
public class SerializerSerializeBenchmark
{
private SimpleModel _simple = null!;
private FastModel _fast = null!;
private NestedModel _nested = null!;
private Byte[] _buffer = null!;
private MemoryStream _stream = null!;
[GlobalSetup]
public void Setup()
{
_simple = new SimpleModel
{
Id = 42,
Name = "NewLife",
IsActive = true,
CreatedAt = new DateTime(2025, 1, 1, 0, 0, 0, DateTimeKind.Utc),
Score = 99.5,
};
_fast = new FastModel
{
Id = 42,
Name = "NewLife",
IsActive = true,
CreatedAt = new DateTime(2025, 1, 1, 0, 0, 0, DateTimeKind.Utc),
Score = 99.5,
};
_nested = new NestedModel
{
Id = 1,
Title = "Hello World",
Inner = _simple,
Timestamp = DateTimeOffset.UtcNow.ToUnixTimeSeconds(),
};
_buffer = new Byte[512];
_stream = new MemoryStream(_buffer);
}
// ── SpanSerializer(反射) ─────────────────────────────
[Benchmark(Description = "Span_Simple序列化_到Span", Baseline = true)]
public Int32 Span_Serialize_Simple_ToSpan() =>
SpanSerializer.Serialize(_simple, _buffer);
[Benchmark(Description = "Span_Simple序列化_池化包")]
public void Span_Serialize_Simple_Pool()
{
using var pk = SpanSerializer.Serialize(_simple);
}
[Benchmark(Description = "Span_Nested序列化_到Span")]
public Int32 Span_Serialize_Nested_ToSpan() =>
SpanSerializer.Serialize(_nested, _buffer);
// ── SpanSerializer(ISpanSerializable) ───────────────
[Benchmark(Description = "Span_Fast序列化_到Span")]
public Int32 Span_Serialize_Fast_ToSpan() =>
SpanSerializer.Serialize(_fast, _buffer);
[Benchmark(Description = "Span_Fast序列化_池化包")]
public void Span_Serialize_Fast_Pool()
{
using var pk = SpanSerializer.Serialize(_fast);
}
// ── Binary ──────────────────────────────────────────
[Benchmark(Description = "Binary_Simple序列化")]
public void Binary_Serialize_Simple()
{
_stream.Position = 0;
Binary.FastWrite(_simple, _stream);
}
[Benchmark(Description = "Binary_Nested序列化")]
public void Binary_Serialize_Nested()
{
_stream.Position = 0;
Binary.FastWrite(_nested, _stream);
}
}
// -------------------------------------------------------
// 反序列化基准测试
// -------------------------------------------------------
/// <summary>SpanSerializer vs Binary 反序列化性能对比</summary>
[MemoryDiagnoser]
[SimpleJob]
public class SerializerDeserializeBenchmark
{
private Byte[] _spanSimpleBytes = null!;
private Byte[] _spanFastBytes = null!;
private Byte[] _spanNestedBytes = null!;
private Byte[] _binarySimpleBytes = null!;
private Byte[] _binaryNestedBytes = null!;
[GlobalSetup]
public void Setup()
{
var simple = new SimpleModel
{
Id = 42,
Name = "NewLife",
IsActive = true,
CreatedAt = new DateTime(2025, 1, 1, 0, 0, 0, DateTimeKind.Utc),
Score = 99.5,
};
var fast = new FastModel
{
Id = 42,
Name = "NewLife",
IsActive = true,
CreatedAt = new DateTime(2025, 1, 1, 0, 0, 0, DateTimeKind.Utc),
Score = 99.5,
};
var nested = new NestedModel
{
Id = 1,
Title = "Hello World",
Inner = simple,
Timestamp = DateTimeOffset.UtcNow.ToUnixTimeSeconds(),
};
// SpanSerializer — 序列化为字节
var buf = new Byte[512];
var n = SpanSerializer.Serialize(simple, buf);
_spanSimpleBytes = buf[..n];
n = SpanSerializer.Serialize(fast, buf);
_spanFastBytes = buf[..n];
n = SpanSerializer.Serialize(nested, buf);
_spanNestedBytes = buf[..n];
// Binary — 序列化为字节
using var ms1 = new MemoryStream();
Binary.FastWrite(simple, ms1);
_binarySimpleBytes = ms1.ToArray();
using var ms2 = new MemoryStream();
Binary.FastWrite(nested, ms2);
_binaryNestedBytes = ms2.ToArray();
}
// ── SpanSerializer(反射) ─────────────────────────────
[Benchmark(Description = "Span_Simple反序列化", Baseline = true)]
public SimpleModel Span_Deserialize_Simple() =>
SpanSerializer.Deserialize<SimpleModel>(_spanSimpleBytes);
[Benchmark(Description = "Span_Nested反序列化")]
public NestedModel Span_Deserialize_Nested() =>
SpanSerializer.Deserialize<NestedModel>(_spanNestedBytes);
// ── SpanSerializer(ISpanSerializable) ───────────────
[Benchmark(Description = "Span_Fast反序列化")]
public FastModel Span_Deserialize_Fast() =>
SpanSerializer.Deserialize<FastModel>(_spanFastBytes);
// ── Binary ──────────────────────────────────────────
[Benchmark(Description = "Binary_Simple反序列化")]
public SimpleModel? Binary_Deserialize_Simple()
{
using var ms = new MemoryStream(_binarySimpleBytes);
return Binary.FastRead<SimpleModel>(ms);
}
[Benchmark(Description = "Binary_Nested反序列化")]
public NestedModel? Binary_Deserialize_Nested()
{
using var ms = new MemoryStream(_binaryNestedBytes);
return Binary.FastRead<NestedModel>(ms);
}
}
// -------------------------------------------------------
// 批量操作基准测试
// -------------------------------------------------------
/// <summary>SpanSerializer vs Binary 批量序列化/反序列化性能对比</summary>
[MemoryDiagnoser]
[SimpleJob]
public class SerializerBatchBenchmark
{
private SimpleModel[] _models = null!;
private Byte[] _buffer = null!;
private Byte[] _spanBatchBytes = null!;
private Byte[] _binaryBatchBytes = null!;
[Params(100, 1000)]
public Int32 Count { get; set; }
[GlobalSetup]
public void Setup()
{
_models = new SimpleModel[Count];
for (var i = 0; i < Count; i++)
{
_models[i] = new SimpleModel
{
Id = i,
Name = $"Item_{i}",
IsActive = i % 2 == 0,
CreatedAt = new DateTime(2025, 1, 1, 0, 0, 0, DateTimeKind.Utc).AddSeconds(i),
Score = i * 0.5,
};
}
// 预分配足够大的缓冲区(每个对象约 40 字节,留余量)
_buffer = new Byte[Count * 64];
// 预序列化批量数据用于反序列化测试
using var msSrc = new MemoryStream(_buffer);
var bn = new Binary(msSrc) { EncodeInt = true };
foreach (var m in _models)
bn.Write(m);
var spanBuf = new Byte[Count * 64];
var writer = new NewLife.Buffers.SpanWriter(spanBuf);
foreach (var m in _models)
SpanSerializer.WriteObject(ref writer, m, typeof(SimpleModel));
_binaryBatchBytes = msSrc.ToArray();
_spanBatchBytes = spanBuf[..writer.WrittenCount];
}
// ── SpanSerializer 批量 ──────────────────────────────
[Benchmark(Description = "Span_批量序列化", Baseline = true)]
public Int32 Span_Serialize_Batch()
{
var writer = new NewLife.Buffers.SpanWriter(_buffer);
foreach (var m in _models)
SpanSerializer.WriteObject(ref writer, m, typeof(SimpleModel));
return writer.WrittenCount;
}
[Benchmark(Description = "Span_批量反序列化")]
public Int32 Span_Deserialize_Batch()
{
var reader = new NewLife.Buffers.SpanReader(_spanBatchBytes);
var count = 0;
while (reader.Available >= 24) // 最小字段保护
{
SpanSerializer.ReadObject(ref reader, typeof(SimpleModel));
count++;
if (count >= Count) break;
}
return count;
}
// ── Binary 批量 ─────────────────────────────────────
[Benchmark(Description = "Binary_批量序列化")]
public Int64 Binary_Serialize_Batch()
{
using var ms = new MemoryStream(_buffer);
var bn = new Binary(ms) { EncodeInt = true };
foreach (var m in _models)
bn.Write(m);
return bn.Total;
}
[Benchmark(Description = "Binary_批量反序列化")]
public Int32 Binary_Deserialize_Batch()
{
using var ms = new MemoryStream(_binaryBatchBytes);
var bn = new Binary(ms) { EncodeInt = true };
var count = 0;
while (!bn.EndOfStream && count < Count)
{
bn.Read<SimpleModel>();
count++;
}
return count;
}
}
// -------------------------------------------------------
// 并发基准测试
// -------------------------------------------------------
/// <summary>SpanSerializer vs Binary 多线程并发吞吐对比</summary>
[MemoryDiagnoser]
[SimpleJob]
public class SerializerConcurrencyBenchmark
{
private SimpleModel _model = null!;
private Byte[] _spanBytes = null!;
private Byte[] _binaryBytes = null!;
public static IEnumerable<Int32> ThreadCounts
{
get
{
var cores = Environment.ProcessorCount;
var set = new SortedSet<Int32> { 1, 4, 8, 32 };
set.Add(cores);
return set;
}
}
[ParamsSource(nameof(ThreadCounts))]
public Int32 ThreadCount { get; set; }
[GlobalSetup]
public void Setup()
{
_model = new SimpleModel
{
Id = 42,
Name = "NewLife",
IsActive = true,
CreatedAt = new DateTime(2025, 1, 1, 0, 0, 0, DateTimeKind.Utc),
Score = 99.5,
};
var buf = new Byte[256];
var n = SpanSerializer.Serialize(_model, buf);
_spanBytes = buf[..n];
using var ms = new MemoryStream();
Binary.FastWrite(_model, ms);
_binaryBytes = ms.ToArray();
}
[Benchmark(Description = "Span_并发序列化", Baseline = true)]
public void Span_Serialize_Concurrent()
{
Parallel.For(0, ThreadCount, _ =>
{
Span<Byte> buf = stackalloc Byte[256];
SpanSerializer.Serialize(_model, buf);
});
}
[Benchmark(Description = "Span_并发反序列化")]
public void Span_Deserialize_Concurrent()
{
Parallel.For(0, ThreadCount, _ =>
{
SpanSerializer.Deserialize<SimpleModel>(_spanBytes);
});
}
[Benchmark(Description = "Binary_并发序列化")]
public void Binary_Serialize_Concurrent()
{
Parallel.For(0, ThreadCount, _ =>
{
using var ms = new MemoryStream(256);
Binary.FastWrite(_model, ms);
});
}
[Benchmark(Description = "Binary_并发反序列化")]
public void Binary_Deserialize_Concurrent()
{
Parallel.For(0, ThreadCount, _ =>
{
using var ms = new MemoryStream(_binaryBytes);
Binary.FastRead<SimpleModel>(ms);
});
}
}
|